Merge ^/head r309170 through r309212.

This commit is contained in:
Dimitry Andric 2016-11-27 14:27:51 +00:00
commit d6d16831c5
79 changed files with 20638 additions and 6616 deletions

View File

@ -142,6 +142,8 @@ OLD_FILES+=usr/lib/clang/3.9.0/lib/freebsd/libclang_rt.ubsan_standalone_cxx-x86_
OLD_DIRS+=usr/lib/clang/3.9.0/lib/freebsd OLD_DIRS+=usr/lib/clang/3.9.0/lib/freebsd
OLD_DIRS+=usr/lib/clang/3.9.0/lib OLD_DIRS+=usr/lib/clang/3.9.0/lib
OLD_DIRS+=usr/lib/clang/3.9.0 OLD_DIRS+=usr/lib/clang/3.9.0
# 20161127: Remove vm_page_cache(9)
OLD_FILES+=usr/share/man/man9/vm_page_cache.9.gz
# 20161124: new clang import which bumps version from 3.8.0 to 3.9.0. # 20161124: new clang import which bumps version from 3.8.0 to 3.9.0.
OLD_FILES+=usr/lib/clang/3.8.0/include/sanitizer/allocator_interface.h OLD_FILES+=usr/lib/clang/3.8.0/include/sanitizer/allocator_interface.h
OLD_FILES+=usr/lib/clang/3.8.0/include/sanitizer/asan_interface.h OLD_FILES+=usr/lib/clang/3.8.0/include/sanitizer/asan_interface.h

View File

@ -1,3 +1,3 @@
/* $FreeBSD$ */ /* $FreeBSD$ */
#define FREEBSD_CC_VERSION 1200004 #define FREEBSD_CC_VERSION 1200005

View File

@ -28,7 +28,7 @@
.\" @(#)mmap.2 8.4 (Berkeley) 5/11/95 .\" @(#)mmap.2 8.4 (Berkeley) 5/11/95
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd February 18, 2015 .Dd November 25, 2016
.Dt MMAP 2 .Dt MMAP 2
.Os .Os
.Sh NAME .Sh NAME
@ -189,6 +189,8 @@ this option any VM pages you dirty may be flushed to disk every so often
(every 30-60 seconds usually) which can create performance problems if you (every 30-60 seconds usually) which can create performance problems if you
do not need that to occur (such as when you are using shared file-backed do not need that to occur (such as when you are using shared file-backed
mmap regions for IPC purposes). mmap regions for IPC purposes).
Dirty data will be flushed automatically when all mappings of an object are
removed and all descriptors referencing the object are closed.
Note that VM/file system coherency is Note that VM/file system coherency is
maintained whether you use maintained whether you use
.Dv MAP_NOSYNC .Dv MAP_NOSYNC

View File

@ -352,7 +352,6 @@ MAN= accept_filter.9 \
vm_page_alloc.9 \ vm_page_alloc.9 \
vm_page_bits.9 \ vm_page_bits.9 \
vm_page_busy.9 \ vm_page_busy.9 \
vm_page_cache.9 \
vm_page_deactivate.9 \ vm_page_deactivate.9 \
vm_page_dontneed.9 \ vm_page_dontneed.9 \
vm_page_aflag.9 \ vm_page_aflag.9 \

View File

@ -1,51 +0,0 @@
.\"
.\" Copyright (C) 2001 Chad David <davidc@acns.ab.ca>. 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(s), this list of conditions and the following disclaimer as
.\" the first lines of this file unmodified other than the possible
.\" addition of one or more copyright notices.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice(s), this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
.\" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
.\" DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
.\" DIRECT, INDIRECT, INCIDENTAL, 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
.\" DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd July 24, 2001
.Dt VM_PAGE_CACHE 9
.Os
.Sh NAME
.Nm vm_page_cache
.Nd "move a page onto the cache queue"
.Sh SYNOPSIS
.In sys/param.h
.In vm/vm.h
.In vm/vm_page.h
.Ft void
.Fn vm_page_cache "vm_page_t m"
.Sh DESCRIPTION
The
.Fn vm_page_cache
function moves a page onto the cache queue,
and removes any protection that may be set on the page.
If the page is busy, wired or unmanaged then the move
to the cache queue will fail.
If the page is dirty the system will panic.
.Sh AUTHORS
This manual page was written by
.An Chad David Aq Mt davidc@acns.ab.ca .

View File

@ -78,6 +78,8 @@ __FBSDID("$FreeBSD$");
#define CH1_CLK_DIV_RATIO_M_SHIFT 0 #define CH1_CLK_DIV_RATIO_M_SHIFT 0
#define TCON_PLLREF 3000000ULL #define TCON_PLLREF 3000000ULL
#define TCON_PLLREF_FRAC1 297000000ULL
#define TCON_PLLREF_FRAC2 270000000ULL
#define TCON_PLL_M_MIN 1 #define TCON_PLL_M_MIN 1
#define TCON_PLL_M_MAX 15 #define TCON_PLL_M_MAX 15
#define TCON_PLL_N_MIN 9 #define TCON_PLL_N_MIN 9
@ -290,7 +292,7 @@ aw_lcdclk_recalc_freq(struct clknode *clk, uint64_t *freq)
} }
static void static void
calc_tcon_pll(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn) calc_tcon_pll_integer(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn)
{ {
int64_t diff, fcur, best; int64_t diff, fcur, best;
int m, n; int m, n;
@ -309,15 +311,87 @@ calc_tcon_pll(uint64_t fin, uint64_t fout, uint32_t *pm, uint32_t *pn)
} }
} }
static int
calc_tcon_pll_fractional(uint64_t fin, uint64_t fout, int *clk_div)
{
int m;
/* Test for 1X match */
for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
if (fout == (fin / m)) {
*clk_div = m;
return (CH0_CLK_SRC_SEL_PLL3_1X);
}
}
/* Test for 2X match */
for (m = TCON_PLL_M_MIN; m <= TCON_PLL_M_MAX; m++) {
if (fout == ((fin * 2) / m)) {
*clk_div = m;
return (CH0_CLK_SRC_SEL_PLL3_2X);
}
}
return (-1);
}
static int
calc_tcon_pll(uint64_t fin, uint64_t fout, uint64_t *pll_freq, int *tcon_pll_div)
{
uint32_t m, m2, n, n2;
uint64_t fsingle, fdouble;
int src_sel;
bool dbl;
/* Test fractional freq first */
src_sel = calc_tcon_pll_fractional(TCON_PLLREF_FRAC1, fout,
tcon_pll_div);
if (src_sel != -1) {
*pll_freq = TCON_PLLREF_FRAC1;
return src_sel;
}
src_sel = calc_tcon_pll_fractional(TCON_PLLREF_FRAC2, fout,
tcon_pll_div);
if (src_sel != -1) {
*pll_freq = TCON_PLLREF_FRAC2;
return src_sel;
}
m = n = m2 = n2 = 0;
dbl = false;
/* Find the frequency closes to the target dot clock, using
* both 1X and 2X PLL inputs as possible candidates.
*/
calc_tcon_pll_integer(TCON_PLLREF, fout, &m, &n);
calc_tcon_pll_integer(TCON_PLLREF * 2, fout, &m2, &n2);
fsingle = m ? (n * TCON_PLLREF) / m : 0;
fdouble = m2 ? (n2 * TCON_PLLREF * 2) / m2 : 0;
if (fdouble > fsingle) {
dbl = true;
m = m2;
n = n2;
}
/* Set desired parent frequency */
*pll_freq = n * TCON_PLLREF;
*tcon_pll_div = m;
/* Return the desired source clock */
return (dbl ? CH0_CLK_SRC_SEL_PLL3_2X :
CH0_CLK_SRC_SEL_PLL3_1X);
}
static int static int
aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
int flags, int *stop) int flags, int *stop)
{ {
struct aw_lcdclk_softc *sc; struct aw_lcdclk_softc *sc;
uint32_t val, m, m2, n, n2, src_sel; uint64_t pll_freq;
uint64_t fsingle, fdouble; uint32_t val, src_sel;
int error; int error, tcon_pll_div;
bool dbl;
sc = clknode_get_softc(clk); sc = clknode_get_softc(clk);
@ -329,26 +403,7 @@ aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
if (sc->id != CLK_IDX_CH1_SCLK2) if (sc->id != CLK_IDX_CH1_SCLK2)
return (ENXIO); return (ENXIO);
m = n = m2 = n2 = 0; src_sel = calc_tcon_pll(fin, *fout, &pll_freq, &tcon_pll_div);
dbl = false;
/* Find the frequency closes to the target dot clock, using
* both 1X and 2X PLL inputs as possible candidates.
*/
calc_tcon_pll(TCON_PLLREF, *fout, &m, &n);
calc_tcon_pll(TCON_PLLREF * 2, *fout, &m2, &n2);
fsingle = m ? (n * TCON_PLLREF) / m : 0;
fdouble = m2 ? (n2 * TCON_PLLREF * 2) / m2 : 0;
if (fdouble > fsingle) {
dbl = true;
m = m2;
n = n2;
}
src_sel = dbl ? CH0_CLK_SRC_SEL_PLL3_2X :
CH0_CLK_SRC_SEL_PLL3_1X;
/* Switch parent clock if necessary */ /* Switch parent clock if necessary */
if (src_sel != clknode_get_parent_idx(clk)) { if (src_sel != clknode_get_parent_idx(clk)) {
@ -357,10 +412,8 @@ aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
return (error); return (error);
} }
/* Set desired parent frequency */ error = clknode_set_freq(clknode_get_parent(clk), pll_freq,
fin = n * TCON_PLLREF; 0, 0);
error = clknode_set_freq(clknode_get_parent(clk), fin, 0, 0);
if (error != 0) if (error != 0)
return (error); return (error);
@ -369,7 +422,7 @@ aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
return (error); return (error);
/* Fetch new input frequency */ /* Fetch new input frequency */
error = clknode_get_freq(clknode_get_parent(clk), &fin); error = clknode_get_freq(clknode_get_parent(clk), &pll_freq);
if (error != 0) if (error != 0)
return (error); return (error);
@ -377,11 +430,11 @@ aw_lcdclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
DEVICE_LOCK(sc); DEVICE_LOCK(sc);
LCDCLK_READ(sc, &val); LCDCLK_READ(sc, &val);
val &= ~CH1_CLK_DIV_RATIO_M; val &= ~CH1_CLK_DIV_RATIO_M;
val |= ((m - 1) << CH1_CLK_DIV_RATIO_M_SHIFT); val |= ((tcon_pll_div - 1) << CH1_CLK_DIV_RATIO_M_SHIFT);
LCDCLK_WRITE(sc, val); LCDCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc); DEVICE_UNLOCK(sc);
*fout = fin / m; *fout = pll_freq / tcon_pll_div;
*stop = 1; *stop = 1;
break; break;

View File

@ -482,11 +482,20 @@ a10_pll3_set_freq(struct aw_pll_sc *sc, uint64_t fin, uint64_t *fout,
{ {
uint32_t val, m, mode, func; uint32_t val, m, mode, func;
m = *fout / A10_PLL3_REF_FREQ; if (*fout == 297000000) {
func = A10_PLL3_FUNC_SET_297MHZ;
mode = A10_PLL3_MODE_SEL_INT; mode = A10_PLL3_MODE_SEL_FRACT;
func = 0; m = 0;
*fout = m * A10_PLL3_REF_FREQ; } else if (*fout == 270000000) {
func = A10_PLL3_FUNC_SET_270MHZ;
mode = A10_PLL3_MODE_SEL_FRACT;
m = 0;
} else {
mode = A10_PLL3_MODE_SEL_INT;
func = 0;
m = *fout / A10_PLL3_REF_FREQ;
*fout = m * A10_PLL3_REF_FREQ;
}
DEVICE_LOCK(sc); DEVICE_LOCK(sc);
PLL_READ(sc, &val); PLL_READ(sc, &val);

View File

@ -298,7 +298,7 @@
reg = <0x00000001>; reg = <0x00000001>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
ethernet { ethernet: ethernet {
compatible = "net,ethernet", compatible = "net,ethernet",
"usb,device"; "usb,device";
reg = <0x00000001>; reg = <0x00000001>;
@ -386,6 +386,7 @@
aliases { aliases {
uart0 = &uart0; uart0 = &uart0;
ethernet0 = &ethernet;
}; };
chosen { chosen {

View File

@ -315,7 +315,7 @@
reg = <0x00000001>; reg = <0x00000001>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
ethernet { ethernet: ethernet {
compatible = "net,ethernet", compatible = "net,ethernet",
"usb,device"; "usb,device";
reg = <0x00000001>; reg = <0x00000001>;
@ -397,6 +397,7 @@
aliases { aliases {
uart0 = &uart0; uart0 = &uart0;
ethernet0 = &ethernet;
}; };
chosen { chosen {

View File

@ -1220,14 +1220,22 @@ dev/bhnd/cores/pmu/bhnd_pmu.c optional bhnd
dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_core.c optional bhnd
dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_if.m optional bhnd
dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd dev/bhnd/cores/pmu/bhnd_pmu_subr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_common.c optional bhnd dev/bhnd/nvram/bhnd_nvram_data_bcm.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_cfe.c optional bhnd siba_nexus cfe | \ dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c optional bhnd
bhnd bcma_nexus cfe dev/bhnd/nvram/bhnd_nvram_data_btxt.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_data_sprom.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_data_tlv.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd dev/bhnd/nvram/bhnd_nvram_if.m optional bhnd
dev/bhnd/nvram/bhnd_nvram_parser.c optional bhnd dev/bhnd/nvram/bhnd_nvram_io.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_iobuf.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_iores.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_store.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_subr.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_value.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_value_fmts.c optional bhnd
dev/bhnd/nvram/bhnd_nvram_value_prf.c optional bhnd
dev/bhnd/nvram/bhnd_sprom.c optional bhnd dev/bhnd/nvram/bhnd_sprom.c optional bhnd
dev/bhnd/nvram/bhnd_sprom_parser.c optional bhnd
dev/bhnd/siba/siba.c optional siba bhnd dev/bhnd/siba/siba.c optional siba bhnd
dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb dev/bhnd/siba/siba_bhndb.c optional siba bhnd bhndb
dev/bhnd/siba/siba_erom.c optional siba bhnd dev/bhnd/siba/siba_erom.c optional siba bhnd

View File

@ -1057,7 +1057,8 @@ bhnd_nvram_getvar_str(device_t dev, const char *name, char *buf, size_t len,
int error; int error;
larg = len; larg = len;
error = bhnd_nvram_getvar(dev, name, buf, &larg, BHND_NVRAM_TYPE_CSTR); error = bhnd_nvram_getvar(dev, name, buf, &larg,
BHND_NVRAM_TYPE_STRING);
if (rlen != NULL) if (rlen != NULL)
*rlen = larg; *rlen = larg;

View File

@ -1,189 +0,0 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* BHND CFE NVRAM driver.
*
* Provides access to device NVRAM via CFE.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/bhnd/bhnd.h>
#include <dev/cfe/cfe_api.h>
#include <dev/cfe/cfe_error.h>
#include <dev/cfe/cfe_ioctl.h>
#include "bhnd_nvram_if.h"
#include "bhnd_nvramvar.h"
/**
* Default bhnd_nvram driver implementation of DEVICE_PROBE().
*/
int
bhnd_nvram_probe(device_t dev)
{
device_set_desc(dev, "Broadcom NVRAM");
/* Refuse wildcard attachments */
return (BUS_PROBE_NOWILDCARD);
}
/**
* Call from subclass DEVICE_ATTACH() implementations to handle
* device attachment.
*
* @param dev BHND NVRAM device.
* @param data NVRAM data to be copied and parsed. No reference to data
* will be held after return.
* @param size Size of @p data, in bytes.
* @param fmt NVRAM format.
*/
int
bhnd_nvram_attach(device_t dev, void *data, size_t size, bhnd_nvram_format fmt)
{
struct bhnd_nvram_softc *sc;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
/* Initialize NVRAM parser */
error = bhnd_nvram_parser_init(&sc->nvram, dev, data, size, fmt);
if (error)
return (error);
/* Initialize mutex */
BHND_NVRAM_LOCK_INIT(sc);
return (0);
}
/**
* Default bhnd_nvram driver implementation of DEVICE_RESUME().
*/
int
bhnd_nvram_resume(device_t dev)
{
return (0);
}
/**
* Default bhnd_nvram driver implementation of DEVICE_SUSPEND().
*/
int
bhnd_nvram_suspend(device_t dev)
{
return (0);
}
/**
* Default bhnd_nvram driver implementation of DEVICE_DETACH().
*/
int
bhnd_nvram_detach(device_t dev)
{
struct bhnd_nvram_softc *sc;
sc = device_get_softc(dev);
bhnd_nvram_parser_fini(&sc->nvram);
BHND_NVRAM_LOCK_DESTROY(sc);
return (0);
}
/**
* Default bhnd_nvram driver implementation of BHND_NVRAM_GETVAR().
*/
static int
bhnd_nvram_getvar_method(device_t dev, const char *name, void *buf, size_t *len,
bhnd_nvram_type type)
{
struct bhnd_nvram_softc *sc;
int error;
sc = device_get_softc(dev);
BHND_NVRAM_LOCK(sc);
error = bhnd_nvram_parser_getvar(&sc->nvram, name, buf, len, type);
BHND_NVRAM_UNLOCK(sc);
return (error);
}
/**
* Default bhnd_nvram driver implementation of BHND_NVRAM_SETVAR().
*/
static int
bhnd_nvram_setvar_method(device_t dev, const char *name, const void *buf,
size_t len, bhnd_nvram_type type)
{
struct bhnd_nvram_softc *sc;
int error;
sc = device_get_softc(dev);
BHND_NVRAM_LOCK(sc);
error = bhnd_nvram_parser_setvar(&sc->nvram, name, buf, len, type);
BHND_NVRAM_UNLOCK(sc);
return (error);
}
static device_method_t bhnd_nvram_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhnd_nvram_probe),
DEVMETHOD(device_resume, bhnd_nvram_resume),
DEVMETHOD(device_suspend, bhnd_nvram_suspend),
DEVMETHOD(device_detach, bhnd_nvram_detach),
/* NVRAM interface */
DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_getvar_method),
DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_setvar_method),
DEVMETHOD_END
};
DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_driver, bhnd_nvram_methods, sizeof(struct bhnd_nvram_softc));

View File

@ -32,11 +32,17 @@
#ifndef _BHND_NVRAM_BHND_NVRAM_H_ #ifndef _BHND_NVRAM_BHND_NVRAM_H_
#define _BHND_NVRAM_BHND_NVRAM_H_ #define _BHND_NVRAM_BHND_NVRAM_H_
#ifdef _KERNEL
#include <sys/types.h>
#else /* !_KERNEL */
#include <stdbool.h>
#include <stdint.h>
#endif /* _KERNEL */
/** /**
* NVRAM data sources supported by bhnd(4) devices. * NVRAM data sources supported by bhnd(4) devices.
*/ */
typedef enum { typedef enum {
BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable BHND_NVRAM_SRC_OTP, /**< On-chip one-time-programmable
* memory. */ * memory. */
@ -67,50 +73,52 @@ typedef enum {
*/ */
} bhnd_nvram_src; } bhnd_nvram_src;
/** Supported NVRAM formats. */ /**
* NVRAM data types.
*
* @internal
*
* All primitive (non-array) constants should be representable as a 4-bit
* integer (e.g. 0-15) to support SPROM_OPCODE_TYPE_IMM encoding as used by
* nvram_map_gen.awk.
*/
typedef enum { typedef enum {
BHND_NVRAM_FMT_BCM = 0, /**< Broadcom NUL-delimited key=value pairs */ BHND_NVRAM_TYPE_UINT8 = 0, /**< unsigned 8-bit integer */
BHND_NVRAM_FMT_TLV = 1, /**< CFE TLV encoding, as used on WGT634U */ BHND_NVRAM_TYPE_UINT16 = 1, /**< unsigned 16-bit integer */
BHND_NVRAM_FMT_BTXT = 2, /**< Broadcom board text file. This is used BHND_NVRAM_TYPE_UINT32 = 2, /**< unsigned 32-bit integer */
to provide external NVRAM data for some BHND_NVRAM_TYPE_UINT64 = 3, /**< signed 64-bit integer */
fullmac WiFi devices. */ BHND_NVRAM_TYPE_INT8 = 4, /**< signed 8-bit integer */
BHND_NVRAM_FMT_SPROM = 3, /**< SPROM/OTP-specific encoding used by BHND_NVRAM_TYPE_INT16 = 5, /**< signed 16-bit integer */
Broadcom network adapters */ BHND_NVRAM_TYPE_INT32 = 6, /**< signed 32-bit integer */
BHND_NVRAM_FMT_CIS = 4, /**< A mostly CIS-compatible encoding used BHND_NVRAM_TYPE_INT64 = 7, /**< signed 64-bit integer */
on some Broadcom network adapters */ BHND_NVRAM_TYPE_CHAR = 8, /**< ASCII/UTF-8 character */
BHND_NVRAM_FMT_UNKNOWN = 5 /**< Unknown or unrecognized format */ BHND_NVRAM_TYPE_STRING = 9, /**< ASCII/UTF-8 NUL-terminated
} bhnd_nvram_format; string */
/* 10-15 reserved for primitive (non-array) types */
/** bhnd_nvram_type bit flags */ BHND_NVRAM_TYPE_UINT8_ARRAY = 16, /**< array of uint8 integers */
enum { BHND_NVRAM_TYPE_UINT16_ARRAY = 17, /**< array of uint16 integers */
BHND_NVRAM_TF_SIGNED = (1<<7), BHND_NVRAM_TYPE_UINT32_ARRAY = 18, /**< array of uint32 integers */
}; BHND_NVRAM_TYPE_UINT64_ARRAY = 19, /**< array of uint64 integers */
BHND_NVRAM_TYPE_INT8_ARRAY = 20, /**< array of int8 integers */
#define BHND_NVRAM_TYPE_ID_MASK 0xF BHND_NVRAM_TYPE_INT16_ARRAY = 21, /**< array of int16 integers */
#define BHND_NVRAM_TYPE_FLAGS_MASK 0x70 BHND_NVRAM_TYPE_INT32_ARRAY = 22, /**< array of int32 integers */
BHND_NVRAM_TYPE_INT64_ARRAY = 23, /**< array of int64 integers */
#define BHND_NVRAM_TYPE_ID(_id, _flags) \ BHND_NVRAM_TYPE_CHAR_ARRAY = 24, /**< array of ASCII/UTF-8
(((_id) & BHND_NVRAM_TYPE_ID_MASK) | \ characters */
((_flags) & BHND_NVRAM_TYPE_FLAGS_MASK)) BHND_NVRAM_TYPE_STRING_ARRAY = 25, /**< array of ASCII/UTF-8
NUL-terminated strings */
/** Supported NVRAM data types */
typedef enum {
BHND_NVRAM_TYPE_UINT8 = BHND_NVRAM_TYPE_ID(0, 0), /**< unsigned 8-bit integer */
BHND_NVRAM_TYPE_UINT16 = BHND_NVRAM_TYPE_ID(1, 0), /**< unsigned 16-bit integer */
BHND_NVRAM_TYPE_UINT32 = BHND_NVRAM_TYPE_ID(2, 0), /**< unsigned 32-bit integer */
BHND_NVRAM_TYPE_INT8 = BHND_NVRAM_TYPE_ID(4, BHND_NVRAM_TF_SIGNED), /**< signed 8-bit integer */
BHND_NVRAM_TYPE_INT16 = BHND_NVRAM_TYPE_ID(5, BHND_NVRAM_TF_SIGNED), /**< signed 16-bit integer */
BHND_NVRAM_TYPE_INT32 = BHND_NVRAM_TYPE_ID(6, BHND_NVRAM_TF_SIGNED), /**< signed 32-bit integer */
BHND_NVRAM_TYPE_CHAR = BHND_NVRAM_TYPE_ID(7, BHND_NVRAM_TF_SIGNED), /**< ASCII character */
BHND_NVRAM_TYPE_CSTR = BHND_NVRAM_TYPE_ID(8, 0), /**< NUL-terminated C string */
} bhnd_nvram_type; } bhnd_nvram_type;
#undef BHND_NVRAM_TYPE_ID_MASK const char *bhnd_nvram_string_array_next(const char *inp, size_t ilen,
#undef BHND_NVRAM_TYPE_FLAGS_MASK const char *prev);
#undef BHND_NVRAM_TYPE_ID
#define BHND_NVRAM_SIGNED_TYPE(_type) \ bool bhnd_nvram_is_signed_type(bhnd_nvram_type type);
(((_type) & BHND_NVRAM_TF_SIGNED) == BHND_NVRAM_TF_SIGNED) bool bhnd_nvram_is_unsigned_type(bhnd_nvram_type type);
bool bhnd_nvram_is_int_type(bhnd_nvram_type type);
bool bhnd_nvram_is_array_type(bhnd_nvram_type type);
bhnd_nvram_type bhnd_nvram_base_type(bhnd_nvram_type type);
const char *bhnd_nvram_type_name(bhnd_nvram_type type);
#endif /* _BHND_NVRAM_BHND_NVRAM_H_ */ #endif /* _BHND_NVRAM_BHND_NVRAM_H_ */

View File

@ -1,373 +0,0 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* BHND CFE NVRAM driver.
*
* Provides access to device NVRAM via CFE.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/bhnd/bhnd.h>
#include <dev/cfe/cfe_api.h>
#include <dev/cfe/cfe_error.h>
#include <dev/cfe/cfe_ioctl.h>
#include "bhnd_nvram_if.h"
#include "bhnd_nvramvar.h"
static int nvram_open_cfedev(device_t dev, char *devname, int fd,
int64_t *offset, uint32_t *size, bhnd_nvram_format fmt);
static char *nvram_find_cfedev(device_t dev, int *fd, int64_t *offset,
uint32_t *size, bhnd_nvram_format *fmt);
/** Known CFE NVRAM device names, in probe order. */
static char *nvram_cfe_devs[] = {
"nflash0.nvram", /* NAND */
"nflash1.nvram",
"flash0.nvram",
"flash1.nvram",
};
/** Supported CFE NVRAM formats, in probe order. */
bhnd_nvram_format nvram_cfe_fmts[] = {
BHND_NVRAM_FMT_BCM,
BHND_NVRAM_FMT_TLV
};
static int
bhnd_nvram_cfe_probe(device_t dev)
{
char *devname;
bhnd_nvram_format fmt;
int64_t offset;
uint32_t size;
int error;
int fd;
/* Defer to default driver implementation */
if ((error = bhnd_nvram_probe(dev)) > 0)
return (error);
/* Locate a usable CFE device */
devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt);
if (devname == NULL)
return (ENXIO);
cfe_close(fd);
switch (fmt) {
case BHND_NVRAM_FMT_BCM:
device_set_desc(dev, "Broadcom NVRAM");
break;
case BHND_NVRAM_FMT_TLV:
device_set_desc(dev, "Broadcom WGT634U NVRAM");
break;
default:
device_printf(dev, "unknown NVRAM format: %d\n", fmt);
return (ENXIO);
}
/* Refuse wildcard attachments */
return (BUS_PROBE_NOWILDCARD);
}
static int
bhnd_nvram_cfe_attach(device_t dev)
{
char *devname;
unsigned char *buffer;
bhnd_nvram_format fmt;
int64_t offset;
uint32_t size;
int error;
int fd;
error = 0;
buffer = NULL;
fd = CFE_ERR;
/* Locate NVRAM device via CFE */
devname = nvram_find_cfedev(dev, &fd, &offset, &size, &fmt);
if (devname == NULL) {
device_printf(dev, "CFE NVRAM device not found\n");
return (ENXIO);
}
/* Copy out NVRAM buffer */
buffer = malloc(size, M_TEMP, M_NOWAIT);
if (buffer == NULL)
return (ENOMEM);
for (size_t remain = size; remain > 0;) {
int nr, req;
req = ulmin(INT_MAX, remain);
nr = cfe_readblk(fd, size-remain, buffer+(size-remain),
req);
if (nr < 0) {
device_printf(dev, "%s: cfe_readblk() failed: %d\n",
devname, fd);
error = ENXIO;
goto cleanup;
}
remain -= nr;
if (nr == 0 && remain > 0) {
device_printf(dev, "%s: cfe_readblk() unexpected EOF: "
"%zu of %zu pending\n", devname, remain, size);
error = ENXIO;
goto cleanup;
}
}
device_printf(dev, "CFE %s (%#jx+%#jx)\n", devname, (uintmax_t)offset,
(uintmax_t)size);
/* Delegate to default driver implementation */
error = bhnd_nvram_attach(dev, buffer, size, fmt);
cleanup:
if (buffer != NULL)
free(buffer, M_TEMP);
if (fd >= 0)
cfe_close(fd);
return (error);
}
/**
* Identify and open a CFE NVRAM device.
*
* @param dev bhnd_nvram_cfe device.
* @param devname The name of the CFE device to be probed.
* @param fd An open CFE file descriptor for @p devname.
* @param[out] offset On success, the NVRAM data offset within @p @fd.
* @param[out] size On success, maximum the NVRAM data size within @p fd.
* @param fmt The expected NVRAM data format for this device.
*
* @retval 0 success
* @retval non-zero If probing @p devname fails, a regular unix
* error code will be returned.
*/
static int
nvram_open_cfedev(device_t dev, char *devname, int fd, int64_t *offset,
uint32_t *size, bhnd_nvram_format fmt)
{
union bhnd_nvram_ident ident;
nvram_info_t nvram_info;
int cerr, devinfo, dtype, rlen;
int error;
/* Try to fetch device info */
if ((devinfo = cfe_getdevinfo(devname)) == CFE_ERR_DEVNOTFOUND)
return (ENODEV);
if (devinfo < 0) {
device_printf(dev, "cfe_getdevinfo() failed: %d",
devinfo);
return (ENXIO);
}
/* Verify device type */
dtype = devinfo & CFE_DEV_MASK;
switch (dtype) {
case CFE_DEV_FLASH:
case CFE_DEV_NVRAM:
/* Valid device type */
break;
default:
device_printf(dev, "%s: unknown device type %d\n",
devname, dtype);
return (ENXIO);
}
/* Try to fetch nvram info from CFE */
cerr = cfe_ioctl(fd, IOCTL_NVRAM_GETINFO, (unsigned char *)&nvram_info,
sizeof(nvram_info), &rlen, 0);
if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
device_printf(dev, "%s: IOCTL_NVRAM_GETINFO failed: %d\n",
devname, cerr);
return (ENXIO);
}
/* Fall back on flash info.
*
* This is known to be required on the Asus RT-N53 (CFE 5.70.55.33,
* BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
* CFE_ERR_INV_COMMAND.
*/
if (cerr == CFE_ERR_INV_COMMAND) {
flash_info_t fi;
cerr = cfe_ioctl(fd, IOCTL_FLASH_GETINFO, (unsigned char *)&fi,
sizeof(fi), &rlen, 0);
if (cerr != CFE_OK) {
device_printf(dev, "%s: IOCTL_FLASH_GETINFO failed: "
"%d\n", devname, cerr);
return (ENXIO);
}
nvram_info.nvram_eraseflg =
!(fi.flash_flags & FLASH_FLAG_NOERASE);
nvram_info.nvram_offset = 0x0;
nvram_info.nvram_size = fi.flash_size;
}
/* Try to read NVRAM header/format identification */
cerr = cfe_readblk(fd, 0, (unsigned char *)&ident, sizeof(ident));
if (cerr < 0) {
device_printf(dev, "%s: cfe_readblk() failed: %d\n",
devname, cerr);
return (ENXIO);
} else if (cerr == 0) {
/* EOF */
return (ENODEV);
} else if (cerr != sizeof(ident)) {
device_printf(dev, "%s: cfe_readblk() short read: %d\n",
devname, cerr);
return (ENXIO);
}
/* Verify expected format */
if ((error = bhnd_nvram_parser_identify(&ident, fmt)))
return (error);
/* Provide offset and size */
switch (fmt) {
case BHND_NVRAM_FMT_TLV:
/* No size field is available; must assume the NVRAM data
* consumes up to the full CFE NVRAM range */
*offset = nvram_info.nvram_offset;
*size = nvram_info.nvram_size;
break;
case BHND_NVRAM_FMT_BCM:
if (ident.bcm.size > nvram_info.nvram_size) {
device_printf(dev, "%s: NVRAM size %#x overruns %#x "
"device limit\n", devname, ident.bcm.size,
nvram_info.nvram_size);
return (ENODEV);
}
*offset = nvram_info.nvram_offset;
*size = ident.bcm.size;
break;
default:
return (EINVAL);
}
return (0);
}
/**
* Find (and open) a CFE NVRAM device.
*
* @param dev bhnd_nvram_cfe device.
* @param[out] fd On success, a valid CFE file descriptor. The callee
* is responsible for closing this file descriptor via
* cfe_close().
* @param[out] offset On success, the NVRAM data offset within @p @fd.
* @param[out] size On success, maximum the NVRAM data size within @p fd.
* @param fmt The expected NVRAM data format for this device.
*
* @return On success, the opened CFE device's name will be returned. On
* error, returns NULL.
*/
static char *
nvram_find_cfedev(device_t dev, int *fd, int64_t *offset,
uint32_t *size, bhnd_nvram_format *fmt)
{
char *devname;
int error;
for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
*fmt = nvram_cfe_fmts[i];
for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
devname = nvram_cfe_devs[j];
/* Open for reading */
*fd = cfe_open(devname);
if (*fd == CFE_ERR_DEVNOTFOUND) {
continue;
} else if (*fd < 0) {
device_printf(dev, "%s: cfe_open() failed: "
"%d\n", devname, *fd);
continue;
}
/* Probe */
error = nvram_open_cfedev(dev, devname, *fd, offset,
size, *fmt);
if (error == 0)
return (devname);
/* Keep searching */
devname = NULL;
cfe_close(*fd);
}
}
return (NULL);
}
static device_method_t bhnd_nvram_cfe_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhnd_nvram_cfe_probe),
DEVMETHOD(device_attach, bhnd_nvram_cfe_attach),
DEVMETHOD_END
};
DEFINE_CLASS_1(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
sizeof(struct bhnd_nvram_softc), bhnd_nvram_driver);
EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);

View File

@ -1,661 +0,0 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/ctype.h>
#include <sys/hash.h>
#include <sys/param.h>
#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#include "bhnd_nvram_common.h"
#include "bhnd_nvram_map_data.h"
/*
* Common NVRAM/SPROM support, including NVRAM variable map
* lookup.
*/
MALLOC_DEFINE(M_BHND_NVRAM, "bhnd_nvram", "bhnd nvram data");
/*
* CRC-8 lookup table used to checksum SPROM and NVRAM data via
* bhnd_nvram_crc8().
*
* Generated with following parameters:
* polynomial: CRC-8 (x^8 + x^7 + x^6 + x^4 + x^2 + 1)
* reflected bits: false
* reversed: true
*/
const uint8_t bhnd_nvram_crc8_tab[] = {
0x00, 0xf7, 0xb9, 0x4e, 0x25, 0xd2, 0x9c, 0x6b, 0x4a, 0xbd, 0xf3,
0x04, 0x6f, 0x98, 0xd6, 0x21, 0x94, 0x63, 0x2d, 0xda, 0xb1, 0x46,
0x08, 0xff, 0xde, 0x29, 0x67, 0x90, 0xfb, 0x0c, 0x42, 0xb5, 0x7f,
0x88, 0xc6, 0x31, 0x5a, 0xad, 0xe3, 0x14, 0x35, 0xc2, 0x8c, 0x7b,
0x10, 0xe7, 0xa9, 0x5e, 0xeb, 0x1c, 0x52, 0xa5, 0xce, 0x39, 0x77,
0x80, 0xa1, 0x56, 0x18, 0xef, 0x84, 0x73, 0x3d, 0xca, 0xfe, 0x09,
0x47, 0xb0, 0xdb, 0x2c, 0x62, 0x95, 0xb4, 0x43, 0x0d, 0xfa, 0x91,
0x66, 0x28, 0xdf, 0x6a, 0x9d, 0xd3, 0x24, 0x4f, 0xb8, 0xf6, 0x01,
0x20, 0xd7, 0x99, 0x6e, 0x05, 0xf2, 0xbc, 0x4b, 0x81, 0x76, 0x38,
0xcf, 0xa4, 0x53, 0x1d, 0xea, 0xcb, 0x3c, 0x72, 0x85, 0xee, 0x19,
0x57, 0xa0, 0x15, 0xe2, 0xac, 0x5b, 0x30, 0xc7, 0x89, 0x7e, 0x5f,
0xa8, 0xe6, 0x11, 0x7a, 0x8d, 0xc3, 0x34, 0xab, 0x5c, 0x12, 0xe5,
0x8e, 0x79, 0x37, 0xc0, 0xe1, 0x16, 0x58, 0xaf, 0xc4, 0x33, 0x7d,
0x8a, 0x3f, 0xc8, 0x86, 0x71, 0x1a, 0xed, 0xa3, 0x54, 0x75, 0x82,
0xcc, 0x3b, 0x50, 0xa7, 0xe9, 0x1e, 0xd4, 0x23, 0x6d, 0x9a, 0xf1,
0x06, 0x48, 0xbf, 0x9e, 0x69, 0x27, 0xd0, 0xbb, 0x4c, 0x02, 0xf5,
0x40, 0xb7, 0xf9, 0x0e, 0x65, 0x92, 0xdc, 0x2b, 0x0a, 0xfd, 0xb3,
0x44, 0x2f, 0xd8, 0x96, 0x61, 0x55, 0xa2, 0xec, 0x1b, 0x70, 0x87,
0xc9, 0x3e, 0x1f, 0xe8, 0xa6, 0x51, 0x3a, 0xcd, 0x83, 0x74, 0xc1,
0x36, 0x78, 0x8f, 0xe4, 0x13, 0x5d, 0xaa, 0x8b, 0x7c, 0x32, 0xc5,
0xae, 0x59, 0x17, 0xe0, 0x2a, 0xdd, 0x93, 0x64, 0x0f, 0xf8, 0xb6,
0x41, 0x60, 0x97, 0xd9, 0x2e, 0x45, 0xb2, 0xfc, 0x0b, 0xbe, 0x49,
0x07, 0xf0, 0x9b, 0x6c, 0x22, 0xd5, 0xf4, 0x03, 0x4d, 0xba, 0xd1,
0x26, 0x68, 0x9f
};
/**
* Return the size of type @p type, or 0 if @p type has a variable width
* (e.g. a C string).
*
* @param type NVRAM data type.
* @result the byte width of @p type.
*/
size_t
bhnd_nvram_type_width(bhnd_nvram_type type)
{
switch (type) {
case BHND_NVRAM_TYPE_INT8:
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_CHAR:
return (sizeof(uint8_t));
case BHND_NVRAM_TYPE_INT16:
case BHND_NVRAM_TYPE_UINT16:
return (sizeof(uint16_t));
case BHND_NVRAM_TYPE_INT32:
case BHND_NVRAM_TYPE_UINT32:
return (sizeof(uint32_t));
case BHND_NVRAM_TYPE_CSTR:
return (0);
}
/* Quiesce gcc4.2 */
panic("bhnd nvram type %u unknown", type);
}
/**
* Return the format string to use when printing @p type with @p sfmt
*
* @param type The value type being printed.
* @param sfmt The string format required for @p type.
* @param elem_num The element index being printed. If this is the first
* value in an array of elements, the index would be 0, the next would be 1,
* and so on.
*
* @retval non-NULL A valid printf format string.
* @retval NULL If no format string is available for @p type and @p sfmt.
*/
const char *
bhnd_nvram_type_fmt(bhnd_nvram_type type, bhnd_nvram_sfmt sfmt,
size_t elem_num)
{
size_t width;
width = bhnd_nvram_type_width(type);
/* Sanity-check the type width */
switch (width) {
case 1:
case 2:
case 4:
break;
default:
return (NULL);
}
/* Special-cased string formats */
switch (sfmt) {
case BHND_NVRAM_SFMT_LEDDC:
/* If this is the first element, use the 0x-prefixed
* SFMT_HEX */
if (elem_num == 0)
sfmt = BHND_NVRAM_SFMT_HEX;
break;
default:
break;
}
/* Return the format string */
switch (sfmt) {
case BHND_NVRAM_SFMT_MACADDR:
switch (width) {
case 1: return ("%02" PRIx8);
}
break;
case BHND_NVRAM_SFMT_HEX:
switch (width) {
case 1: return ("0x%02" PRIx8);
case 2: return ("0x%04" PRIx16);
case 4: return ("0x%08" PRIx32);
}
break;
case BHND_NVRAM_SFMT_DEC:
if (BHND_NVRAM_SIGNED_TYPE(type)) {
switch (width) {
case 1: return ("%" PRId8);
case 2: return ("%" PRId16);
case 4: return ("%" PRId32);
}
} else {
switch (width) {
case 1: return ("%" PRIu8);
case 2: return ("%" PRIu16);
case 4: return ("%" PRIu32);
}
}
break;
case BHND_NVRAM_SFMT_LEDDC:
switch (width) {
case 1: return ("%02" PRIx8);
case 2: return ("%04" PRIx16);
case 4: return ("%08" PRIx32);
}
break;
case BHND_NVRAM_SFMT_CCODE:
switch (width) {
case 1: return ("%c");
}
break;
}
return (NULL);
}
/**
* Find and return the variable definition for @p varname, if any.
*
* @param varname variable name
*
* @retval bhnd_nvram_vardefn If a valid definition for @p varname is found.
* @retval NULL If no definition for @p varname is found.
*/
const struct bhnd_nvram_vardefn *
bhnd_nvram_find_vardefn(const char *varname)
{
size_t min, mid, max;
int order;
/*
* Locate the requested variable using a binary search.
*
* The variable table is guaranteed to be sorted in lexicographical
* order (using the 'C' locale for collation rules)
*/
min = 0;
mid = 0;
max = nitems(bhnd_nvram_vardefs) - 1;
while (max >= min) {
/* Select midpoint */
mid = (min + max) / 2;
/* Determine which side of the partition to search */
order = strcmp(bhnd_nvram_vardefs[mid].name, varname);
if (order < 0) {
/* Search upper partition */
min = mid + 1;
} else if (order > 0) {
/* Search lower partition */
max = mid - 1;
} else if (order == 0) {
/* Match found */
return (&bhnd_nvram_vardefs[mid]);
}
}
/* Not found */
return (NULL);
}
/**
* Validate an NVRAM variable name.
*
* Scans for special characters (path delimiters, value delimiters, path
* alias prefixes), returning false if the given name cannot be used
* as a relative NVRAM key.
*
* @param name A relative NVRAM variable name to validate.
* @param name_len The length of @p name, in bytes.
*
* @retval true If @p name is a valid relative NVRAM key.
* @retval false If @p name should not be used as a relative NVRAM key.
*/
bool
bhnd_nvram_validate_name(const char *name, size_t name_len)
{
size_t limit;
limit = strnlen(name, name_len);
if (limit == 0)
return (false);
/* Disallow path alias prefixes ([0-9]+:.*) */
if (limit >= 2 && isdigit(*name)) {
for (const char *p = name; p - name < limit; p++) {
if (isdigit(*p))
continue;
else if (*p == ':')
return (false);
else
break;
}
}
/* Scan for special characters */
for (const char *p = name; p - name < limit; p++) {
switch (*p) {
case '/': /* path delimiter */
case '=': /* key=value delimiter */
return (false);
default:
if (isspace(*p) || !isascii(*p))
return (false);
}
}
return (true);
}
/**
* Parse an octet string, such as a MAC address, consisting of hex octets
* separated with ':' or '-'.
*
* @param value The octet string to parse.
* @param value_len The length of @p value, in bytes.
* @param buf The output buffer to which parsed octets will be written. May be
* NULL.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
* @param type
*/
int
bhnd_nvram_parse_octet_string(const char *value, size_t value_len, void *buf,
size_t *len, bhnd_nvram_type type)
{
size_t limit, nbytes, width;
size_t slen;
uint8_t octet;
char delim;
slen = strnlen(value, value_len);
nbytes = 0;
if (buf != NULL)
limit = *len;
else
limit = 0;
/* Type must have a fixed width */
if ((width = bhnd_nvram_type_width(type)) == 0)
return (EINVAL);
/* String length (not including NUL) must be aligned on an octet
* boundary ('AA:BB', not 'AA:B', etc), and must be large enough
* to contain at least two octet entries. */
if (slen % 3 != 2 || slen < sizeof("AA:BB") - 1)
return (EINVAL);
/* Identify the delimiter used. The standard delimiter for
* MAC addresses is ':', but some earlier NVRAM formats may use
* '-' */
switch ((delim = value[2])) {
case ':':
case '-':
break;
default:
return (EINVAL);
}
/* Parse octets */
for (const char *p = value; p - value < value_len; p++) {
void *outp;
size_t pos;
unsigned char c;
pos = (p - value);
/* Skip delimiter after each octet */
if (pos % 3 == 2) {
if (*p == delim)
continue;
if (*p == '\0')
return (0);
/* No delimiter? */
return (EINVAL);
}
c = *(const unsigned char *)p;
if (isdigit(c))
c -= '0';
else if (isxdigit(c))
c -= islower(c) ? 'a' - 10 : 'A' - 10;
else
return (EINVAL);
if (pos % 3 == 0) {
/* MSB */
octet = (c << 4);
continue;
} else if (pos % 3 == 1) {
/* LSB */
octet |= (c & 0xF);
}
/* Skip writing? */
if (limit < width || limit - width < nbytes) {
nbytes += width;
continue;
}
/* Write output */
outp = ((uint8_t *)buf) + nbytes;
switch (type) {
case BHND_NVRAM_TYPE_UINT8:
*(uint8_t *)outp = octet;
break;
case BHND_NVRAM_TYPE_UINT16:
*(uint16_t *)outp = octet;
break;
case BHND_NVRAM_TYPE_UINT32:
*(uint32_t *)outp = octet;
break;
case BHND_NVRAM_TYPE_INT8:
if (octet > INT8_MAX)
return (ERANGE);
*(int8_t *)outp = (int8_t)octet;
break;
case BHND_NVRAM_TYPE_INT16:
*(int16_t *)outp = (int8_t)octet;
break;
case BHND_NVRAM_TYPE_INT32:
*(int32_t *)outp = (int8_t)octet;
break;
case BHND_NVRAM_TYPE_CHAR:
#if (CHAR_MAX < UINT8_MAX)
if (octet > CHAR_MAX)
return (ERANGE);
#endif
*(char *)outp = (char)octet;
break;
default:
printf("unknown type %d\n", type);
return (EINVAL);
}
nbytes += width;
}
return (0);
}
/**
* Initialize a new variable hash table with @p nelements.
*
* @param map Hash table instance to be initialized.
* @param nelements The number of hash table buckets to allocate.
* @param flags Hash table flags (HASH_*).
*/
int
bhnd_nvram_varmap_init(struct bhnd_nvram_varmap *map, size_t nelements,
int flags)
{
map->table = hashinit_flags(nelements, M_BHND_NVRAM, &map->mask,
flags);
if (map->table == NULL)
return (ENOMEM);
return (0);
}
/**
* Deallocate all resources associated with @p map.
*
* @param map Hash table to be deallocated.
*/
void
bhnd_nvram_varmap_free(struct bhnd_nvram_varmap *map)
{
struct bhnd_nvram_tuple *t, *tnext;
/* Free all elements */
for (size_t i = 0; i <= map->mask; i++) {
LIST_FOREACH_SAFE(t, &map->table[i], t_link, tnext) {
LIST_REMOVE(t, t_link);
bhnd_nvram_tuple_free(t);
}
}
/* Free hash table */
hashdestroy(map->table, M_BHND_NVRAM, map->mask);
}
/**
* Add a variable entry to @p map.
*
* @param map Hash table to modify.
* @param name Variable name.
* @param value Variable value.
* @param value_len The length of @p value, in bytes.
*
* @retval 0 success
* @retval ENOMEM unable to allocate new entry
*/
int
bhnd_nvram_varmap_add(struct bhnd_nvram_varmap *map, const char *name,
const char *value, size_t value_len)
{
struct bhnd_nvram_tuples *head;
struct bhnd_nvram_tuple *t;
/* Locate target bucket */
head = &map->table[hash32_str(name, HASHINIT) & map->mask];
/* Allocate new entry */
if ((t = bhnd_nvram_tuple_alloc(name, value)) == NULL)
return (ENOMEM);
/* Remove any existing entry */
bhnd_nvram_varmap_remove(map, name);
/* Insert new entry */
LIST_INSERT_HEAD(head, t, t_link);
return (0);
}
/**
* Remove @p map in @p tuples, if it exists.
*
* @param map Hash table to modify.
* @param key Key to remove.
*
* @retval 0 success
* @retval ENOENT If @p name is not found in @p map.
*/
int
bhnd_nvram_varmap_remove(struct bhnd_nvram_varmap *map, const char *name)
{
struct bhnd_nvram_tuples *head;
struct bhnd_nvram_tuple *t;
size_t name_len;
/* Locate target bucket */
head = &map->table[hash32_str(name, HASHINIT) & map->mask];
name_len = strlen(name);
LIST_FOREACH(t, head, t_link) {
if (t->name_len != name_len)
continue;
if (strncmp(t->name, name, name_len) != 0)
continue;
LIST_REMOVE(t, t_link);
bhnd_nvram_tuple_free(t);
return (0);
}
/* Not found */
return (ENOENT);
}
/**
* Search for @p name in @p map.
*
* @param map Hash table to modify.
* @param name Variable name.
* @param name_len Length of @p name, not including trailing NUL.
*
* @retval bhnd_nvram_tuple If @p name is found in @p map.
* @retval NULL If @p name is not found.
*/
struct bhnd_nvram_tuple *
bhnd_nvram_varmap_find(struct bhnd_nvram_varmap *map, const char *name,
size_t name_len)
{
struct bhnd_nvram_tuples *head;
struct bhnd_nvram_tuple *t;
head = &map->table[hash32_str(name, HASHINIT) & map->mask];
LIST_FOREACH(t, head, t_link) {
if (t->name_len != name_len)
continue;
if (strncmp(t->name, name, name_len) != 0)
continue;
/* Match */
return (t);
}
/* not found */
return (NULL);
}
/**
* Check for @p name in @p map.
*
* @param map Hash table to modify.
* @param name Variable name.
* @param name_len Length of @p name, not including trailing NUL.
*
* @retval true If @p name is found in @p tuples.
* @retval false If @p name is not found.
*/
bool bhnd_nvram_varmap_contains(struct bhnd_nvram_varmap *map,
const char *name, size_t name_len)
{
return (bhnd_nvram_varmap_find(map, name, name_len) != NULL);
}
/**
* Allocate a new tuple with @p name and @p value.
*
* @param name Variable name.
* @param value Variable value.
*
* @retval bhnd_nvram_tuple success.
* @retval NULL if allocation fails.
*/
struct bhnd_nvram_tuple *
bhnd_nvram_tuple_alloc(const char *name, const char *value)
{
struct bhnd_nvram_tuple *t;
t = malloc(sizeof(*t), M_BHND_NVRAM, M_NOWAIT);
if (t == NULL)
return (NULL);
t->name_len = strlen(name);
t->name = malloc(t->name_len+1, M_BHND_NVRAM, M_NOWAIT);
t->value_len = strlen(value);
t->value = malloc(t->value_len+1, M_BHND_NVRAM, M_NOWAIT);
if (t->name == NULL || t->value == NULL)
goto failed;
strcpy(t->name, name);
strcpy(t->value, value);
return (t);
failed:
if (t->name != NULL)
free(t->name, M_BHND_NVRAM);
if (t->value != NULL)
free(t->value, M_BHND_NVRAM);
free(t, M_BHND_NVRAM);
return (NULL);
}
void
bhnd_nvram_tuple_free(struct bhnd_nvram_tuple *tuple)
{
free(tuple->name, M_BHND_NVRAM);
free(tuple->value, M_BHND_NVRAM);
free(tuple, M_BHND_NVRAM);
}

View File

@ -1,180 +0,0 @@
/*-
* 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_COMMON_H_
#define _BHND_NVRAM_BHND_NVRAM_COMMON_H_
#include <sys/param.h>
#include <sys/malloc.h>
#include "bhnd_nvram.h"
struct bhnd_nvram_tuple;
struct bhnd_nvram_varmap;
struct bhnd_nvram_vardefn;
MALLOC_DECLARE(M_BHND_NVRAM);
extern const uint8_t bhnd_nvram_crc8_tab[];
#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */
#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */
#define BHND_SPROMREV_MAX UINT8_MAX /**< maximum supported SPROM revision */
/** NVRAM data type string representations */
typedef enum {
BHND_NVRAM_SFMT_HEX = 1, /**< hex format */
BHND_NVRAM_SFMT_DEC = 2, /**< decimal format */
BHND_NVRAM_SFMT_MACADDR = 3, /**< mac address (canonical form, hex octets,
separated with ':') */
BHND_NVRAM_SFMT_LEDDC = 4, /**< LED PWM duty-cycle (2 bytes -- on/off) */
BHND_NVRAM_SFMT_CCODE = 5 /**< count code format (2-3 ASCII chars, or hex string) */
} bhnd_nvram_sfmt;
size_t bhnd_nvram_type_width(bhnd_nvram_type type);
const char *bhnd_nvram_type_fmt(bhnd_nvram_type type,
bhnd_nvram_sfmt sfmt, size_t elem_num);
const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname);
bool bhnd_nvram_validate_name(const char *name,
size_t name_len);
int bhnd_nvram_parse_octet_string(
const char *value, size_t value_len,
void *buf, size_t *len,
bhnd_nvram_type type);
int bhnd_nvram_varmap_init(
struct bhnd_nvram_varmap *map,
size_t nelements, int flags);
void bhnd_nvram_varmap_free(
struct bhnd_nvram_varmap *map);
int bhnd_nvram_varmap_add(
struct bhnd_nvram_varmap *map,
const char *name, const char *value,
size_t value_len);
int bhnd_nvram_varmap_remove(
struct bhnd_nvram_varmap *map,
const char *name);
struct bhnd_nvram_tuple *bhnd_nvram_varmap_find(
struct bhnd_nvram_varmap *map,
const char *name, size_t name_len);
bool bhnd_nvram_varmap_contains(
struct bhnd_nvram_varmap *map,
const char *name, size_t name_len);
struct bhnd_nvram_tuple *bhnd_nvram_tuple_alloc(const char *name,
const char *value);
void bhnd_nvram_tuple_free(
struct bhnd_nvram_tuple *tuple);
/** NVRAM variable flags */
enum {
BHND_NVRAM_VF_ARRAY = (1<<0), /**< variable is an array */
BHND_NVRAM_VF_MFGINT = (1<<1), /**< mfg-internal variable; should not be externally visible */
BHND_NVRAM_VF_IGNALL1 = (1<<2) /**< hide variable if its value has all bits set. */
};
/** SPROM revision compatibility declaration */
struct bhnd_sprom_compat {
uint8_t first; /**< first compatible SPROM revision */
uint8_t last; /**< last compatible SPROM revision, or BHND_SPROMREV_MAX */
};
/** SPROM value descriptor */
struct bhnd_sprom_offset {
uint16_t offset; /**< byte offset within SPROM */
bool cont:1; /**< value should be bitwise OR'd with the
* previous offset descriptor */
bhnd_nvram_type type:7; /**< data type */
int8_t shift; /**< shift to be applied to the value */
uint32_t mask; /**< mask to be applied to the value(s) */
};
/** SPROM-specific variable definition */
struct bhnd_sprom_vardefn {
struct bhnd_sprom_compat compat; /**< sprom compatibility declaration */
const struct bhnd_sprom_offset *offsets; /**< offset descriptors */
size_t num_offsets; /**< number of offset descriptors */
};
/** NVRAM variable definition */
struct bhnd_nvram_vardefn {
const char *name; /**< variable name */
bhnd_nvram_type type; /**< base data type */
bhnd_nvram_sfmt sfmt; /**< string format */
uint32_t flags; /**< BHND_NVRAM_VF_* flags */
const struct bhnd_sprom_vardefn *sp_defs; /**< SPROM-specific variable definitions */
size_t num_sp_defs; /**< number of sprom definitions */
};
/**
* NVRAM value tuple.
*/
struct bhnd_nvram_tuple {
char *name; /**< variable name. */
size_t name_len; /**< variable length. */
char *value; /**< value, or NULL if this tuple represents variable
deletion */
size_t value_len; /**< value length. */
LIST_ENTRY(bhnd_nvram_tuple) t_link;
};
LIST_HEAD(bhnd_nvram_tuples, bhnd_nvram_tuple);
/** NVRAM tuple hash table */
struct bhnd_nvram_varmap {
struct bhnd_nvram_tuples *table; /**< hash buckets */
u_long mask; /**< hash index mask */
};
/**
* Calculate CRC-8 over @p buf.
*
* @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);
}
#endif /* _BHND_NVRAM_BHND_NVRAM_COMMON_H_ */

View File

@ -0,0 +1,527 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data.h"
/**
* Return a human-readable description for the given NVRAM data class.
*
* @param cls The NVRAM class.
*/
const char *
bhnd_nvram_data_class_desc(bhnd_nvram_data_class_t *cls)
{
return (cls->desc);
}
/**
* Probe to see if this NVRAM data class class supports the data mapped by the
* given I/O context, returning a BHND_NVRAM_DATA_PROBE probe result.
*
* @param cls The NVRAM class.
* @param io An I/O context mapping the NVRAM data.
*
* @retval 0 if this is the only possible NVRAM data class for @p io.
* @retval negative if the probe succeeds, a negative value should be returned;
* the class returning the highest negative value should be selected to handle
* NVRAM parsing.
* @retval ENXIO If the NVRAM format is not handled by @p cls.
* @retval positive if an error occurs during probing, a regular unix error
* code should be returned.
*/
int
bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls, struct bhnd_nvram_io *io)
{
return (cls->op_probe(io));
}
/**
* Probe to see if an NVRAM data class in @p classes supports parsing
* of the data mapped by @p io, returning the parsed data in @p data.
*
* The caller is responsible for deallocating the returned instance via
* bhnd_nvram_data_release().
*
* @param[out] data On success, the parsed NVRAM data instance.
* @param io An I/O context mapping the NVRAM data to be copied and parsed.
* @param classes An array of NVRAM data classes to be probed, or NULL to
* probe the default supported set.
* @param num_classes The number of NVRAM data classes in @p classes.
*
* @retval 0 success
* @retval ENXIO if no class is found capable of parsing @p io.
* @retval non-zero if an error otherwise occurs during allocation,
* initialization, or parsing of the NVRAM data, a regular unix error code
* will be returned.
*/
int
bhnd_nvram_data_probe_classes(struct bhnd_nvram_data **data,
struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *classes[],
size_t num_classes)
{
bhnd_nvram_data_class_t *cls;
int error, prio, result;
cls = NULL;
prio = 0;
*data = NULL;
/* If class array is NULL, default to our linker set */
if (classes == NULL) {
classes = SET_BEGIN(bhnd_nvram_data_class_set);
num_classes = SET_COUNT(bhnd_nvram_data_class_set);
}
/* Try to find the best data class capable of parsing io */
for (size_t i = 0; i < num_classes; i++) {
bhnd_nvram_data_class_t *next_cls;
next_cls = classes[i];
/* Try to probe */
result = bhnd_nvram_data_probe(next_cls, io);
/* The parser did not match if an error was returned */
if (result > 0)
continue;
/* Lower priority than previous match; keep
* searching */
if (cls != NULL && result <= prio)
continue;
/* Drop any previously parsed data */
if (*data != NULL) {
bhnd_nvram_data_release(*data);
*data = NULL;
}
/* If this is a 'maybe' match, attempt actual parsing to
* verify that this does in fact match */
if (result <= BHND_NVRAM_DATA_PROBE_MAYBE) {
/* If parsing fails, keep searching */
error = bhnd_nvram_data_new(next_cls, data, io);
if (error)
continue;
}
/* Record best new match */
prio = result;
cls = next_cls;
/* Terminate search immediately on
* BHND_NVRAM_DATA_PROBE_SPECIFIC */
if (result == BHND_NVRAM_DATA_PROBE_SPECIFIC)
break;
}
/* If no match, return error */
if (cls == NULL)
return (ENXIO);
/* If the NVRAM data was not parsed above, do so now */
if (*data == NULL) {
if ((error = bhnd_nvram_data_new(cls, data, io)))
return (error);
}
return (0);
}
/**
* Allocate and initialize a new instance of data class @p cls, copying and
* parsing NVRAM data from @p io.
*
* The caller is responsible for releasing the returned parser instance
* reference via bhnd_nvram_data_release().
*
* @param cls If non-NULL, the data class to be allocated. If NULL,
* bhnd_nvram_data_probe_classes() will be used to determine the data format.
* @param[out] nv On success, a pointer to the newly allocated NVRAM data instance.
* @param io An I/O context mapping the NVRAM data to be copied and parsed.
*
* @retval 0 success
* @retval non-zero if an error occurs during allocation or initialization, a
* regular unix error code will be returned.
*/
int
bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls,
struct bhnd_nvram_data **nv, struct bhnd_nvram_io *io)
{
struct bhnd_nvram_data *data;
int error;
/* If NULL, try to identify the appropriate class */
if (cls == NULL)
return (bhnd_nvram_data_probe_classes(nv, io, NULL, 0));
/* Allocate new instance */
BHND_NV_ASSERT(sizeof(struct bhnd_nvram_data) <= cls->size,
("instance size %zu less than minimum %zu", cls->size,
sizeof(struct bhnd_nvram_data)));
data = bhnd_nv_calloc(1, cls->size);
data->cls = cls;
refcount_init(&data->refs, 1);
/* Let the class handle initialization */
if ((error = cls->op_new(data, io))) {
bhnd_nv_free(data);
return (error);
}
*nv = data;
return (0);
}
/**
* Retain and return a reference to the given data instance.
*
* @param nv The reference to be retained.
*/
struct bhnd_nvram_data *
bhnd_nvram_data_retain(struct bhnd_nvram_data *nv)
{
refcount_acquire(&nv->refs);
return (nv);
}
/**
* Release a reference to the given data instance.
*
* If this is the last reference, the data instance and its associated
* resources will be freed.
*
* @param nv The reference to be released.
*/
void
bhnd_nvram_data_release(struct bhnd_nvram_data *nv)
{
if (!refcount_release(&nv->refs))
return;
/* Free any internal resources */
nv->cls->op_free(nv);
/* Free the instance allocation */
bhnd_nv_free(nv);
}
/**
* Return a pointer to @p nv's data class.
*
* @param nv The NVRAM data instance to be queried.
*/
bhnd_nvram_data_class_t *
bhnd_nvram_data_class(struct bhnd_nvram_data *nv)
{
return (nv->cls);
}
/**
* Return the number of variables in @p nv.
*
* @param nv The NVRAM data to be queried.
*/
size_t
bhnd_nvram_data_count(struct bhnd_nvram_data *nv)
{
return (nv->cls->op_count(nv));
}
/**
* Compute the size of the serialized form of @p nv.
*
* Serialization may be performed via bhnd_nvram_data_serialize().
*
* @param nv The NVRAM data to be queried.
* @param[out] len On success, will be set to the computed size.
*
* @retval 0 success
* @retval non-zero if computing the serialized size otherwise fails, a
* regular unix error code will be returned.
*/
int
bhnd_nvram_data_size(struct bhnd_nvram_data *nv, size_t *len)
{
return (nv->cls->op_size(nv, len));
}
/**
* Serialize the NVRAM data to @p buf, using the NVRAM data class' native
* format.
*
* The resulting serialization may be reparsed with @p nv's BHND NVRAM data
* class.
*
* @param nv The NVRAM data to be serialized.
* @param[out] buf On success, the serialed NVRAM data will be
* written to this buffer. This argment may be
* NULL if the value is not desired.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual length of the serialized data.
*
* @retval 0 success
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
* small to hold the serialized data.
* @retval non-zero If serialization otherwise fails, a regular unix error
* code will be returned.
*/
int
bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
void *buf, size_t *len)
{
return (nv->cls->op_serialize(nv, buf, len));
}
/**
* Return the capability flags (@see BHND_NVRAM_DATA_CAP_*) for @p nv.
*
* @param nv The NVRAM data to be queried.
*/
uint32_t
bhnd_nvram_data_caps(struct bhnd_nvram_data *nv)
{
return (nv->cls->op_caps(nv));
}
/**
* Iterate over @p nv, returning the names of subsequent variables.
*
* @param nv The NVRAM data to be iterated.
* @param[in,out] cookiep A pointer to a cookiep value previously returned
* by bhnd_nvram_data_next(), or a NULL value to
* begin iteration.
*
* @return Returns the next variable name, or NULL if there are no more
* variables defined in @p nv.
*/
const char *
bhnd_nvram_data_next(struct bhnd_nvram_data *nv, void **cookiep)
{
return (nv->cls->op_next(nv, cookiep));
}
/**
* Search @p nv for a named variable, returning the variable's opaque reference
* if found, or NULL if unavailable.
*
* The BHND_NVRAM_DATA_CAP_INDEXED capability flag will be returned by
* bhnd_nvram_data_caps() if @p nv supports effecient name-based
* lookups.
*
* @param nv The NVRAM data to search.
* @param name The name to search for.
*
* @retval non-NULL If @p name is found, the opaque cookie value will be
* returned.
* @retval NULL If @p name is not found.
*/
void *
bhnd_nvram_data_find(struct bhnd_nvram_data *nv, const char *name)
{
return (nv->cls->op_find(nv, name));
}
/**
* A generic implementation of bhnd_nvram_data_find().
*
* This implementation will use bhnd_nvram_data_next() to perform a
* simple O(n) case-insensitve search for @p name.
*/
void *
bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv, const char *name)
{
const char *next;
void *cookiep;
cookiep = NULL;
while ((next = bhnd_nvram_data_next(nv, &cookiep))) {
if (strcasecmp(name, next) == 0)
return (cookiep);
}
/* Not found */
return (NULL);
}
/**
* Read a variable and decode as @p type.
*
* @param nv The NVRAM data.
* @param cookiep An NVRAM variable cookie previously returned
* via bhnd_nvram_data_next() or
* bhnd_nvram_data_find().
* @param[out] buf On success, the requested value will be written
* to this buffer. This argment may be NULL if
* the value is not desired.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
* @param type The data type to be written to @p buf.
*
* @retval 0 success
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
* small to hold the requested value.
* @retval EFTYPE If the variable data cannot be coerced to @p type.
* @retval ERANGE If value coercion would overflow @p type.
*/
int
bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
{
return (nv->cls->op_getvar(nv, cookiep, buf, len, type));
}
/**
* A generic implementation of bhnd_nvram_data_getvar().
*
* This implementation will call bhnd_nvram_data_getvar_ptr() to fetch
* a pointer to the variable data and perform data coercion on behalf
* of the caller.
*
* If a variable definition for the requested variable is available via
* bhnd_nvram_find_vardefn(), the definition will be used to provide
* formatting hints to bhnd_nvram_coerce_value().
*/
int
bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv, void *cookiep,
void *outp, size_t *olen, bhnd_nvram_type otype)
{
bhnd_nvram_val_t val;
const struct bhnd_nvram_vardefn *vdefn;
const bhnd_nvram_val_fmt_t *fmt;
const char *name;
const void *vptr;
bhnd_nvram_type vtype;
size_t vlen;
int error;
BHND_NV_ASSERT(bhnd_nvram_data_caps(nv) & BHND_NVRAM_DATA_CAP_READ_PTR,
("instance does not advertise READ_PTR support"));
/* Fetch pointer to our variable data */
vptr = bhnd_nvram_data_getvar_ptr(nv, cookiep, &vlen, &vtype);
if (vptr == NULL)
return (EINVAL);
/* Use the NVRAM string support */
switch (vtype) {
case BHND_NVRAM_TYPE_STRING:
case BHND_NVRAM_TYPE_STRING_ARRAY:
fmt = &bhnd_nvram_val_bcm_string_fmt;
break;
default:
fmt = NULL;
}
/* Check the variable definition table for a matching entry; if
* it exists, use it to populate the value format. */
name = bhnd_nvram_data_getvar_name(nv, cookiep);
vdefn = bhnd_nvram_find_vardefn(name);
if (vdefn != NULL)
fmt = vdefn->fmt;
/* Attempt value coercion */
error = bhnd_nvram_val_init(&val, fmt, vptr, vlen, vtype,
BHND_NVRAM_VAL_BORROW_DATA);
if (error)
return (error);
error = bhnd_nvram_val_encode(&val, outp, olen, otype);
/* Clean up */
bhnd_nvram_val_release(&val);
return (error);
}
/**
* If available and supported by the NVRAM data instance, return a reference
* to the internal buffer containing an entry's variable data,
*
* Note that string values may not be NUL terminated.
*
* @param nv The NVRAM data.
* @param cookiep An NVRAM variable cookie previously returned
* via bhnd_nvram_data_next() or
* bhnd_nvram_data_find().
* @param[out] len On success, will be set to the actual size of
* the requested value.
* @param[out] type The data type of the entry data.
*
* @retval non-NULL success
* @retval NULL if direct data access is unsupported by @p nv, or
* unavailable for @p cookiep.
*/
const void *
bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
{
return (nv->cls->op_getvar_ptr(nv, cookiep, len, type));
}
/**
* Return the variable name associated with a given @p cookiep.
* @param nv The NVRAM data to be iterated.
* @param[in,out] cookiep A pointer to a cookiep value previously returned
* via bhnd_nvram_data_next() or
* bhnd_nvram_data_find().
*
* @return Returns the variable's name.
*/
const char *
bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
return (nv->cls->op_getvar_name(nv, cookiep));
}

View File

@ -0,0 +1,137 @@
/*-
* 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_DATA_H_
#define _BHND_NVRAM_BHND_NVRAM_DATA_H_
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/bus.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#endif /* _KERNEL */
#include "bhnd_nvram.h"
#include "bhnd_nvram_io.h"
/* NVRAM data class */
typedef struct bhnd_nvram_data_class bhnd_nvram_data_class_t;
/* NVRAM data instance */
struct bhnd_nvram_data;
/** Declare a bhnd_nvram_data_class with name @p _n */
#define BHND_NVRAM_DATA_CLASS_DECL(_n) \
extern struct bhnd_nvram_data_class bhnd_nvram_ ## _n ## _class
BHND_NVRAM_DATA_CLASS_DECL(bcm);
BHND_NVRAM_DATA_CLASS_DECL(bcmraw);
BHND_NVRAM_DATA_CLASS_DECL(tlv);
BHND_NVRAM_DATA_CLASS_DECL(btxt);
BHND_NVRAM_DATA_CLASS_DECL(sprom);
/** bhnd_nvram_data capabilities */
enum {
/** Supports efficient lookup of variables by name */
BHND_NVRAM_DATA_CAP_INDEXED = (1<<0),
/** Supports direct access to backing buffer */
BHND_NVRAM_DATA_CAP_READ_PTR = (1<<1),
/** Supports device path prefixed variables */
BHND_NVRAM_DATA_CAP_DEVPATHS = (1<<2),
};
/**
* A standard set of probe priorities returned by bhnd_nvram_data_probe().
*
* Priority is defined in ascending order, with 0 being the highest priority.
* Return values greater than zero are interpreted as regular unix error codes.
*/
enum {
BHND_NVRAM_DATA_PROBE_MAYBE = -40, /**< Possible match */
BHND_NVRAM_DATA_PROBE_DEFAULT = -20, /**< Definite match of a base
OS-supplied data class */
BHND_NVRAM_DATA_PROBE_SPECIFIC = 0, /**< Terminate search and use
this data class for
parsing */
};
const char *bhnd_nvram_data_class_desc(
bhnd_nvram_data_class_t *cls);
int bhnd_nvram_data_probe(bhnd_nvram_data_class_t *cls,
struct bhnd_nvram_io *io);
int bhnd_nvram_data_probe_classes(
struct bhnd_nvram_data **data,
struct bhnd_nvram_io *io,
bhnd_nvram_data_class_t *classes[],
size_t num_classes);
int bhnd_nvram_data_new(bhnd_nvram_data_class_t *cls,
struct bhnd_nvram_data **nv,
struct bhnd_nvram_io *io);
struct bhnd_nvram_data *bhnd_nvram_data_retain(struct bhnd_nvram_data *nv);
void bhnd_nvram_data_release(struct bhnd_nvram_data *nv);
bhnd_nvram_data_class_t *bhnd_nvram_data_class(struct bhnd_nvram_data *nv);
size_t bhnd_nvram_data_count(struct bhnd_nvram_data *nv);
int bhnd_nvram_data_size(struct bhnd_nvram_data *nv,
size_t *size);
int bhnd_nvram_data_serialize(struct bhnd_nvram_data *nv,
void *buf, size_t *len);
uint32_t bhnd_nvram_data_caps(struct bhnd_nvram_data *nv);
const char *bhnd_nvram_data_next(struct bhnd_nvram_data *nv,
void **cookiep);
void *bhnd_nvram_data_find(struct bhnd_nvram_data *nv,
const char *name);
int bhnd_nvram_data_getvar(struct bhnd_nvram_data *nv,
void *cookiep, void *buf, size_t *len,
bhnd_nvram_type type);
const void *bhnd_nvram_data_getvar_ptr(struct bhnd_nvram_data *nv,
void *cookiep, size_t *len, bhnd_nvram_type *type);
const char *bhnd_nvram_data_getvar_name(struct bhnd_nvram_data *nv,
void *cookiep);
#endif /* _BHND_NVRAM_BHND_NVRAM_DATA_H_ */

View File

@ -0,0 +1,748 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/endian.h>
#ifdef _KERNEL
#include <sys/bus.h>
#include <sys/ctype.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data_bcmreg.h"
#include "bhnd_nvram_data_bcmvar.h"
/*
* Broadcom NVRAM data class.
*
* The Broadcom NVRAM NUL-delimited ASCII format is used by most
* Broadcom SoCs.
*
* The NVRAM data is encoded as a standard header, followed by series of
* NUL-terminated 'key=value' strings; the end of the stream is denoted
* by a single extra NUL character.
*/
struct bhnd_nvram_bcm;
static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar(
struct bhnd_nvram_bcm *bcm,
const char *name);
static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar(
struct bhnd_nvram_bcm *bcm,
void *cookiep);
static size_t bhnd_nvram_bcm_hdrvar_index(
struct bhnd_nvram_bcm *bcm,
struct bhnd_nvram_bcm_hvar *hvar);
/*
* Set of BCM NVRAM header values that are required to be mirrored in the
* NVRAM data itself.
*
* If they're not included in the parsed NVRAM data, we need to vend the
* header-parsed values with their appropriate keys, and add them in any
* updates to the NVRAM data.
*
* If they're modified in NVRAM, we need to sync the changes with the
* the NVRAM header values.
*/
static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
{
.name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
.type = BHND_NVRAM_TYPE_UINT16,
.len = sizeof(uint16_t),
.nelem = 1,
},
{
.name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
.type = BHND_NVRAM_TYPE_UINT16,
.len = sizeof(uint16_t),
.nelem = 1,
},
{
.name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
.type = BHND_NVRAM_TYPE_UINT16,
.len = sizeof(uint16_t),
.nelem = 1,
},
{
.name = BCM_NVRAM_SDRAM_NCDL_VAR,
.type = BHND_NVRAM_TYPE_UINT32,
.len = sizeof(uint32_t),
.nelem = 1,
},
};
/** BCM NVRAM data class instance */
struct bhnd_nvram_bcm {
struct bhnd_nvram_data nv; /**< common instance state */
struct bhnd_nvram_io *data; /**< backing buffer */
/** BCM header values */
struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)];
size_t count; /**< total variable count */
};
BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", sizeof(struct bhnd_nvram_bcm))
static int
bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_bcmhdr hdr;
int error;
if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
return (error);
if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
}
/**
* Initialize @p bcm with the provided NVRAM data mapped by @p src.
*
* @param bcm A newly allocated data instance.
*/
static int
bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
{
struct bhnd_nvram_bcmhdr hdr;
uint8_t *p;
void *ptr;
size_t io_offset, io_size;
uint8_t crc, valid;
int error;
if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
return (error);
if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
return (ENXIO);
/* Fetch the actual NVRAM image size */
io_size = le32toh(hdr.size);
if (io_size < sizeof(hdr)) {
/* The header size must include the header itself */
BHND_NV_LOG("corrupt header size: %zu\n", io_size);
return (EINVAL);
}
if (io_size > bhnd_nvram_io_getsize(src)) {
BHND_NV_LOG("header size %zu exceeds input size %zu\n",
io_size, bhnd_nvram_io_getsize(src));
return (EINVAL);
}
/* Allocate a buffer large enough to hold the NVRAM image, and
* an extra EOF-signaling NUL (on the chance it's missing from the
* source data) */
if (io_size == SIZE_MAX)
return (ENOMEM);
bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
if (bcm->data == NULL)
return (ENOMEM);
/* Fetch a pointer into our backing buffer and copy in the
* NVRAM image. */
error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
if (error)
return (error);
p = ptr;
if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
return (error);
/* Verify the CRC */
valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
if (crc != valid) {
BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
"expected=%hhx)\n", crc, valid);
}
/* Populate header variable definitions */
#define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \
struct bhnd_nvram_bcm_hvar *data; \
data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \
BHND_NV_ASSERT(data != NULL, \
("no such header variable: " __STRING(_name))); \
\
\
data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \
hdr. _name ## _FIELD, _name)); \
} while(0)
BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh);
BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh);
BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh);
BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh);
_Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
"NVRAM header variable(s)");
#undef BCM_READ_HDR_VAR
/* Process the buffer */
bcm->count = 0;
io_offset = sizeof(hdr);
while (io_offset < io_size) {
char *envp;
const char *name, *value;
size_t envp_len;
size_t name_len, value_len;
/* Parse the key=value string */
envp = (char *) (p + io_offset);
envp_len = strnlen(envp, io_size - io_offset);
error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
&name_len, &value, &value_len);
if (error) {
BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
io_offset, error);
return (error);
}
/* Insert a '\0' character, replacing the '=' delimiter and
* allowing us to vend references directly to the variable
* name */
*(envp + name_len) = '\0';
/* Record any NVRAM variables that mirror our header variables.
* This is a brute-force search -- for the amount of data we're
* operating on, it shouldn't be an issue. */
for (size_t i = 0; i < nitems(bcm->hvars); i++) {
struct bhnd_nvram_bcm_hvar *hvar;
union bhnd_nvram_bcm_hvar_value hval;
size_t hval_len;
hvar = &bcm->hvars[i];
/* Already matched? */
if (hvar->envp != NULL)
continue;
/* Name matches? */
if ((strcmp(name, hvar->name)) != 0)
continue;
/* Save pointer to mirrored envp */
hvar->envp = envp;
/* Check for stale value */
hval_len = sizeof(hval);
error = bhnd_nvram_value_coerce(value, value_len,
BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
hvar->type);
if (error) {
/* If parsing fails, we can likely only make
* things worse by trying to synchronize the
* variables */
BHND_NV_LOG("error parsing header variable "
"'%s=%s': %d\n", name, value, error);
} else if (hval_len != hvar->len) {
hvar->stale = true;
} else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
hvar->stale = true;
}
}
/* Seek past the value's terminating '\0' */
io_offset += envp_len;
if (io_offset == io_size) {
BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
io_offset);
return (EINVAL);
}
if (*(p + io_offset) != '\0') {
BHND_NV_LOG("invalid terminator '%#hhx' at offset "
"%#zx\n", *(p + io_offset), io_offset);
return (EINVAL);
}
/* Update variable count */
bcm->count++;
/* Seek to the next record */
if (++io_offset == io_size) {
char ch;
/* Hit EOF without finding a terminating NUL
* byte; we need to grow our buffer and append
* it */
io_size++;
if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
return (error);
/* Write NUL byte */
ch = '\0';
error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
sizeof(ch));
if (error)
return (error);
}
/* Check for explicit EOF (encoded as a single empty NUL
* terminated string) */
if (*(p + io_offset) == '\0')
break;
}
/* Add non-mirrored header variables to total count variable */
for (size_t i = 0; i < nitems(bcm->hvars); i++) {
if (bcm->hvars[i].envp == NULL)
bcm->count++;
}
return (0);
}
static int
bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
{
struct bhnd_nvram_bcm *bcm;
int error;
bcm = (struct bhnd_nvram_bcm *)nv;
/* Populate default BCM mirrored header variable set */
_Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
"hvar declarations must match bhnd_nvram_bcm_hvars template");
memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
/* Parse the BCM input data and initialize our backing
* data representation */
if ((error = bhnd_nvram_bcm_init(bcm, io))) {
bhnd_nvram_bcm_free(nv);
return (error);
}
return (0);
}
static void
bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
if (bcm->data != NULL)
bhnd_nvram_io_free(bcm->data);
}
size_t
bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
return (bcm->count);
}
static int
bhnd_nvram_bcm_size(struct bhnd_nvram_data *nv, size_t *size)
{
return (bhnd_nvram_bcm_serialize(nv, NULL, size));
}
static int
bhnd_nvram_bcm_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
{
struct bhnd_nvram_bcm *bcm;
struct bhnd_nvram_bcmhdr hdr;
void *cookiep;
const char *name;
size_t nbytes, limit;
uint8_t crc;
int error;
bcm = (struct bhnd_nvram_bcm *)nv;
nbytes = 0;
/* Save the output buffer limit */
if (buf == NULL)
limit = 0;
else
limit = *len;
/* Reserve space for the NVRAM header */
nbytes += sizeof(struct bhnd_nvram_bcmhdr);
/* Write all variables to the output buffer */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(nv, &cookiep))) {
uint8_t *outp;
size_t olen;
size_t name_len, val_len;
if (limit > nbytes) {
outp = (uint8_t *)buf + nbytes;
olen = limit - nbytes;
} else {
outp = NULL;
olen = 0;
}
/* Determine length of variable name */
name_len = strlen(name) + 1;
/* Write the variable name and '=' delimiter */
if (olen >= name_len) {
/* Copy name */
memcpy(outp, name, name_len - 1);
/* Append '=' */
*(outp + name_len - 1) = '=';
}
/* Adjust byte counts */
if (SIZE_MAX - name_len < nbytes)
return (ERANGE);
nbytes += name_len;
/* Reposition output */
if (limit > nbytes) {
outp = (uint8_t *)buf + nbytes;
olen = limit - nbytes;
} else {
outp = NULL;
olen = 0;
}
/* Coerce to NUL-terminated C string, writing to the output
* buffer (or just calculating the length if outp is NULL) */
val_len = olen;
error = bhnd_nvram_data_getvar(nv, cookiep, outp, &val_len,
BHND_NVRAM_TYPE_STRING);
if (error && error != ENOMEM)
return (error);
/* Adjust byte counts */
if (SIZE_MAX - val_len < nbytes)
return (ERANGE);
nbytes += val_len;
}
/* Write terminating NUL */
if (nbytes < limit)
*((uint8_t *)buf + nbytes) = '\0';
nbytes++;
/* Provide actual size */
*len = nbytes;
if (buf == NULL || nbytes > limit) {
if (buf != NULL)
return (ENOMEM);
return (0);
}
/* Fetch current NVRAM header */
if ((error = bhnd_nvram_io_read(bcm->data, 0x0, &hdr, sizeof(hdr))))
return (error);
/* Update values covered by CRC and write to output buffer */
hdr.size = htole32(*len);
memcpy(buf, &hdr, sizeof(hdr));
/* Calculate new CRC */
crc = bhnd_nvram_crc8((uint8_t *)buf + BCM_NVRAM_CRC_SKIP,
*len - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
/* Update header with valid CRC */
hdr.cfg0 &= ~BCM_NVRAM_CFG0_CRC_MASK;
hdr.cfg0 |= (crc << BCM_NVRAM_CFG0_CRC_SHIFT);
memcpy(buf, &hdr, sizeof(hdr));
return (0);
}
static uint32_t
bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
{
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
}
static const char *
bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
{
struct bhnd_nvram_bcm *bcm;
struct bhnd_nvram_bcm_hvar *hvar, *hvar_next;
const void *ptr;
const char *envp, *basep;
size_t io_size, io_offset;
int error;
bcm = (struct bhnd_nvram_bcm *)nv;
io_offset = sizeof(struct bhnd_nvram_bcmhdr);
io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
/* Map backing buffer */
error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
NULL);
if (error) {
BHND_NV_LOG("error mapping backing buffer: %d\n", error);
return (NULL);
}
basep = ptr;
/* If cookiep pointers into our header variable array, handle as header
* variable iteration. */
hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
if (hvar != NULL) {
size_t idx;
/* Advance to next entry, if any */
idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
/* Find the next header-defined variable that isn't defined in
* the NVRAM data, start iteration there */
for (size_t i = idx; i < nitems(bcm->hvars); i++) {
hvar_next = &bcm->hvars[i];
if (hvar_next->envp != NULL && !hvar_next->stale)
continue;
*cookiep = hvar_next;
return (hvar_next->name);
}
/* No further header-defined variables; iteration
* complete */
return (NULL);
}
/* Handle standard NVRAM data iteration */
if (*cookiep == NULL) {
/* Start at the first NVRAM data record */
envp = basep;
} else {
/* Seek to next record */
envp = *cookiep;
envp += strlen(envp) + 1; /* key + '\0' */
envp += strlen(envp) + 1; /* value + '\0' */
}
/*
* Skip entries that have an existing header variable entry that takes
* precedence over the NVRAM data value.
*
* The header's value will be provided when performing header variable
* iteration
*/
while ((size_t)(envp - basep) < io_size && *envp != '\0') {
/* Locate corresponding header variable */
hvar = NULL;
for (size_t i = 0; i < nitems(bcm->hvars); i++) {
if (bcm->hvars[i].envp != envp)
continue;
hvar = &bcm->hvars[i];
break;
}
/* If no corresponding hvar entry, or the entry does not take
* precedence over this NVRAM value, we can safely return this
* value as-is. */
if (hvar == NULL || !hvar->stale)
break;
/* Seek to next record */
envp += strlen(envp) + 1; /* key + '\0' */
envp += strlen(envp) + 1; /* value + '\0' */
}
/* On NVRAM data EOF, try switching to header variables */
if ((size_t)(envp - basep) == io_size || *envp == '\0') {
/* Find first valid header variable */
for (size_t i = 0; i < nitems(bcm->hvars); i++) {
if (bcm->hvars[i].envp != NULL)
continue;
*cookiep = &bcm->hvars[i];
return (bcm->hvars[i].name);
}
/* No header variables */
return (NULL);
}
*cookiep = (void *)(uintptr_t)envp;
return (envp);
}
static void *
bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
{
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
{
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static const void *
bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
{
struct bhnd_nvram_bcm *bcm;
struct bhnd_nvram_bcm_hvar *hvar;
const char *envp;
bcm = (struct bhnd_nvram_bcm *)nv;
/* Handle header variables */
if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
BHND_NV_ASSERT(
hvar->len % bhnd_nvram_value_size(hvar->type, NULL, 0,
hvar->nelem) == 0,
("length is not aligned to type width"));
*type = hvar->type;
*len = hvar->len;
return (&hvar->value);
}
/* Cookie points to key\0value\0 -- get the value address */
BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
envp = cookiep;
envp += strlen(envp) + 1; /* key + '\0' */
*len = strlen(envp) + 1; /* value + '\0' */
*type = BHND_NVRAM_TYPE_STRING;
return (envp);
}
static const char *
bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
struct bhnd_nvram_bcm *bcm;
struct bhnd_nvram_bcm_hvar *hvar;
bcm = (struct bhnd_nvram_bcm *)nv;
/* Handle header variables */
if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
return (hvar->name);
}
/* Cookie points to key\0value\0 */
return (cookiep);
}
/**
* Return the internal BCM data reference for a header-defined variable
* with @p name, or NULL if none exists.
*/
static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
{
for (size_t i = 0; i < nitems(bcm->hvars); i++) {
if (strcmp(bcm->hvars[i].name, name) == 0)
return (&bcm->hvars[i]);
}
/* Not found */
return (NULL);
}
/**
* If @p cookiep references a header-defined variable, return the
* internal BCM data reference. Otherwise, returns NULL.
*/
static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
{
#ifdef BHND_NVRAM_INVARIANTS
uintptr_t base, ptr;
#endif
/* If the cookie falls within the hvar array, it's a
* header variable cookie */
if (nitems(bcm->hvars) == 0)
return (NULL);
if (cookiep < (void *)&bcm->hvars[0])
return (NULL);
if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
return (NULL);
#ifdef BHND_NVRAM_INVARIANTS
base = (uintptr_t)bcm->hvars;
ptr = (uintptr_t)cookiep;
BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
#endif /* INVARIANTS */
return ((struct bhnd_nvram_bcm_hvar *)cookiep);
}
/**
* Return the index of @p hdrvar within @p bcm's backing hvars array.
*/
static size_t
bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
struct bhnd_nvram_bcm_hvar *hdrvar)
{
BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
("%p is not a valid hdrvar reference", hdrvar));
return (hdrvar - &bcm->hvars[0]);
}

View File

@ -0,0 +1,380 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/ctype.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
/*
* Broadcom-RAW NVRAM data class.
*
* The Broadcom NVRAM NUL-delimited ASCII format is used by most
* Broadcom SoCs.
*
* The NVRAM data is encoded as a stream of of NUL-terminated 'key=value'
* strings; the end of the stream is denoted by a single extra NUL character.
*/
struct bhnd_nvram_bcmraw;
/** BCM-RAW NVRAM data class instance */
struct bhnd_nvram_bcmraw {
struct bhnd_nvram_data nv; /**< common instance state */
char *data; /**< backing buffer */
size_t size; /**< buffer size */
size_t count; /**< variable count */
};
BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
sizeof(struct bhnd_nvram_bcmraw))
static int
bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
{
char envp[16];
size_t envp_len;
int error;
/*
* Fetch the initial bytes to try to identify BCM data.
*
* We always assert a low probe priority, as we only scan the initial
* bytes of the file.
*/
envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io));
if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
return (error);
/* A zero-length BCM-RAW buffer should contain a single terminating
* NUL */
if (envp_len == 0)
return (ENXIO);
if (envp_len == 1) {
if (envp[0] != '\0')
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_MAYBE);
}
/* Don't match on non-ASCII, non-printable data */
for (size_t i = 0; i < envp_len; i++) {
char c = envp[i];
if (envp[i] == '\0')
break;
if (!bhnd_nv_isprint(c))
return (ENXIO);
}
/* The first character should be a valid key char */
if (!bhnd_nv_isalpha(envp[0]))
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_MAYBE);
}
/**
* Initialize @p bcm with the provided NVRAM data mapped by @p src.
*
* @param bcm A newly allocated data instance.
*/
static int
bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)
{
size_t io_size;
size_t capacity, offset;
int error;
/* Fetch the input image size */
io_size = bhnd_nvram_io_getsize(src);
/* Allocate a buffer large enough to hold the NVRAM image, and
* an extra EOF-signaling NUL (on the chance it's missing from the
* source data) */
if (io_size == SIZE_MAX)
return (ENOMEM);
capacity = io_size + 1 /* room for extra NUL */;
bcm->size = io_size;
if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
return (ENOMEM);
/* Copy in the NVRAM image */
if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
return (error);
/* Process the buffer */
bcm->count = 0;
for (offset = 0; offset < bcm->size; offset++) {
char *envp;
const char *name, *value;
size_t envp_len;
size_t name_len, value_len;
/* Parse the key=value string */
envp = (char *) (bcm->data + offset);
envp_len = strnlen(envp, bcm->size - offset);
error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
&name_len, &value, &value_len);
if (error) {
BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
offset, error);
return (error);
}
/* Insert a '\0' character, replacing the '=' delimiter and
* allowing us to vend references directly to the variable
* name */
*(envp + name_len) = '\0';
/* Add to variable count */
bcm->count++;
/* Seek past the value's terminating '\0' */
offset += envp_len;
if (offset == io_size) {
BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
offset);
return (EINVAL);
}
/* If we hit EOF without finding a terminating NUL
* byte, we need to append it */
if (++offset == bcm->size) {
BHND_NV_ASSERT(offset < capacity,
("appending past end of buffer"));
bcm->size++;
*(bcm->data + offset) = '\0';
}
/* Check for explicit EOF (encoded as a single empty NUL
* terminated string) */
if (*(bcm->data + offset) == '\0')
break;
}
/* Reclaim any unused space in he backing buffer */
if (offset < bcm->size) {
bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);
if (bcm->data == NULL)
return (ENOMEM);
}
return (0);
}
static int
bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
{
struct bhnd_nvram_bcmraw *bcm;
int error;
bcm = (struct bhnd_nvram_bcmraw *)nv;
/* Parse the BCM input data and initialize our backing
* data representation */
if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {
bhnd_nvram_bcmraw_free(nv);
return (error);
}
return (0);
}
static void
bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
if (bcm->data != NULL)
bhnd_nv_free(bcm->data);
}
static size_t
bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
return (bcm->count);
}
static int
bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size)
{
return (bhnd_nvram_bcmraw_serialize(nv, NULL, size));
}
static int
bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
{
struct bhnd_nvram_bcmraw *bcm;
char * const p = (char *)buf;
size_t limit;
size_t offset;
bcm = (struct bhnd_nvram_bcmraw *)nv;
/* Save the output buffer limit */
if (buf == NULL)
limit = 0;
else
limit = *len;
/* The serialized form will be exactly the length
* of our backing buffer representation */
*len = bcm->size;
/* Skip serialization if not requested, or report ENOMEM if
* buffer is too small */
if (buf == NULL) {
return (0);
} else if (*len > limit) {
return (ENOMEM);
}
/* Write all variables to the output buffer */
memcpy(buf, bcm->data, *len);
/* Rewrite all '\0' delimiters back to '=' */
offset = 0;
while (offset < bcm->size) {
size_t name_len, value_len;
name_len = strlen(p + offset);
/* EOF? */
if (name_len == 0) {
BHND_NV_ASSERT(*(p + offset) == '\0',
("no NUL terminator"));
offset++;
break;
}
/* Rewrite 'name\0' to 'name=' */
offset += name_len;
BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset"));
*(p + offset) = '=';
offset++;
value_len = strlen(p + offset);
offset += value_len + 1;
}
return (0);
}
static uint32_t
bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)
{
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
}
static const char *
bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)
{
struct bhnd_nvram_bcmraw *bcm;
const char *envp;
bcm = (struct bhnd_nvram_bcmraw *)nv;
if (*cookiep == NULL) {
/* Start at the first NVRAM data record */
envp = bcm->data;
} else {
/* Seek to next record */
envp = *cookiep;
envp += strlen(envp) + 1; /* key + '\0' */
envp += strlen(envp) + 1; /* value + '\0' */
}
/* EOF? */
if (*envp == '\0')
return (NULL);
*cookiep = (void *)(uintptr_t)envp;
return (envp);
}
static void *
bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
{
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
{
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static const void *
bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
{
const char *envp;
/* Cookie points to key\0value\0 -- get the value address */
envp = cookiep;
envp += strlen(envp) + 1; /* key + '\0' */
*len = strlen(envp) + 1; /* value + '\0' */
*type = BHND_NVRAM_TYPE_STRING;
return (envp);
}
static const char *
bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
/* Cookie points to key\0value\0 */
return (cookiep);
}

View File

@ -0,0 +1,73 @@
/*-
* 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_BCMREG_H_
#define _BHND_NVRAM_BHND_NVRAM_BCMREG_H_
#define BCM_NVRAM_GET_BITS(_value, _field) \
((_value & _field ## _MASK) >> _field ## _SHIFT)
/* BCM NVRAM header fields */
#define BCM_NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
#define BCM_NVRAM_VERSION 1
#define BCM_NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
#define BCM_NVRAM_CFG0_CRC_MASK 0x000000FF
#define BCM_NVRAM_CFG0_CRC_SHIFT 0
#define BCM_NVRAM_CFG0_VER_MASK 0x0000FF00
#define BCM_NVRAM_CFG0_VER_SHIFT 8
#define BCM_NVRAM_CFG0_SDRAM_INIT_FIELD cfg0
#define BCM_NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
#define BCM_NVRAM_CFG0_SDRAM_INIT_SHIFT 16
#define BCM_NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
#define BCM_NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
#define BCM_NVRAM_CFG1_SDRAM_CFG_FIELD cfg1
#define BCM_NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
#define BCM_NVRAM_CFG1_SDRAM_CFG_SHIFT 0
#define BCM_NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
#define BCM_NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FIELD cfg1
#define BCM_NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
#define BCM_NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
#define BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
#define BCM_NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
#define BCM_NVRAM_SDRAM_NCDL_FIELD sdram_ncdl
#define BCM_NVRAM_SDRAM_NCDL_MASK UINT32_MAX
#define BCM_NVRAM_SDRAM_NCDL_SHIFT 0
#define BCM_NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
#define BCM_NVRAM_SDRAM_NCDL_FMT "0x%08x"
#endif /* _BHND_NVRAM_BHND_NVRAM_BCMREG_H_ */

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2016 Landon Fuller <landon@landonf.org> * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -25,40 +25,46 @@
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * 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 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES. * THE POSSIBILITY OF SUCH DAMAGES.
* *
* $FreeBSD$ * $FreeBSD$
*/ */
#ifndef _BHND_NVRAM_SPROM_PARSERVAR_H_ #ifndef _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
#define _BHND_NVRAM_SPROM_PARSERVAR_H_ #define _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_
#include "bhnd_sprom_parser.h" /**
* BCM NVRAM header value data.
*/
union bhnd_nvram_bcm_hvar_value {
uint16_t u16;
uint32_t u32;
};
#define SPROM_SZ_R1_3 128 /**< SPROM image size (rev 1-3) */ /**
#define SPROM_SZ_R4_8_9 440 /**< SPROM image size (rev 4, 8-9) */ * Internal representation of BCM NVRAM values that mirror (and must be
#define SPROM_SZ_R10 460 /**< SPROM image size (rev 10) */ * vended as) NVRAM variables.
#define SPROM_SZ_R11 468 /**< SPROM image size (rev 11) */ */
struct bhnd_nvram_bcm_hvar {
const char *name; /**< variable name */
bhnd_nvram_type type; /**< value type */
size_t nelem; /**< value element count */
size_t len; /**< value length */
const char *envp; /**< Pointer to the NVRAM variable mirroring
this header value, or NULL. */
bool stale; /**< header value does not match
mirrored NVRAM value */
/** Maximum supported SPROM image size */ /** variable data */
#define SPROM_SZ_MAX SPROM_SZ_R11 union bhnd_nvram_bcm_hvar_value value;
};
/** BCM NVRAM header */
struct bhnd_nvram_bcmhdr {
uint32_t magic;
uint32_t size;
uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */
uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */
uint32_t sdram_ncdl; /**< sdram_ncdl */
} __packed;
#define SPROM_SIG_NONE 0x0 #endif /* _BHND_NVRAM_BHND_NVRAM_BCMVAR_H_ */
#define SPROM_SIG_NONE_OFF 0x0
/** SPROM signature (rev 4) */
#define SPROM_SIG_R4 0x5372
#define SPROM_SIG_R4_OFF 64 /**< SPROM signature offset (rev 4) */
/** SPROM signature (rev 8, 9) */
#define SPROM_SIG_R8_9 SPROM_SIG_R4
#define SPROM_SIG_R8_9_OFF 128 /**< SPROM signature offset (rev 8-9) */
/** SPROM signature (rev 10) */
#define SPROM_SIG_R10 SPROM_SIG_R4
#define SPROM_SIG_R10_OFF 438 /**< SPROM signature offset (rev 10) */
/** SPROM signature (rev 11) */
#define SPROM_SIG_R11 0x0634
#define SPROM_SIG_R11_OFF 128 /**< SPROM signature offset (rev 11) */
#endif /* _BHND_NVRAM_SPROM_PARSERVAR_H_ */

View File

@ -0,0 +1,586 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/endian.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/ctype.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data_bcmreg.h" /* for BCM_NVRAM_MAGIC */
/**
* Broadcom "Board Text" data class.
*
* This format is used to provide external NVRAM data for some
* fullmac WiFi devices, and as an input format when programming
* NVRAM/SPROM/OTP.
*/
struct bhnd_nvram_btxt {
struct bhnd_nvram_data nv; /**< common instance state */
struct bhnd_nvram_io *data; /**< memory-backed board text data */
size_t count; /**< variable count */
};
BHND_NVRAM_DATA_CLASS_DEFN(btxt, "Broadcom Board Text",
sizeof(struct bhnd_nvram_btxt))
/** Minimal identification header */
union bhnd_nvram_btxt_ident {
uint32_t bcm_magic;
char btxt[8];
};
static size_t bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt,
void *cookiep);
static int bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io,
size_t offset, size_t *line_len, size_t *env_len);
static int bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io,
size_t *offset);
static int bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io,
size_t *offset);
static int
bhnd_nvram_btxt_probe(struct bhnd_nvram_io *io)
{
union bhnd_nvram_btxt_ident ident;
char c;
int error;
/* Look at the initial header for something that looks like
* an ASCII board text file */
if ((error = bhnd_nvram_io_read(io, 0x0, &ident, sizeof(ident))))
return (error);
/* The BCM NVRAM format uses a 'FLSH' little endian magic value, which
* shouldn't be interpreted as BTXT */
if (le32toh(ident.bcm_magic) == BCM_NVRAM_MAGIC)
return (ENXIO);
/* Don't match on non-ASCII/non-printable data */
for (size_t i = 0; i < nitems(ident.btxt); i++) {
c = ident.btxt[i];
if (!bhnd_nv_isprint(c))
return (ENXIO);
}
/* The first character should either be a valid key char (alpha),
* whitespace, or the start of a comment ('#') */
c = ident.btxt[0];
if (!bhnd_nv_isspace(c) && !bhnd_nv_isalpha(c) && c != '#')
return (ENXIO);
/* We assert a low priority, given that we've only scanned an
* initial few bytes of the file. */
return (BHND_NVRAM_DATA_PROBE_MAYBE);
}
/**
* Initialize @p btxt with the provided board text data mapped by @p src.
*
* @param btxt A newly allocated data instance.
*/
static int
bhnd_nvram_btxt_init(struct bhnd_nvram_btxt *btxt, struct bhnd_nvram_io *src)
{
const void *ptr;
const char *name, *value;
size_t name_len, value_len;
size_t line_len, env_len;
size_t io_offset, io_size, str_size;
int error;
BHND_NV_ASSERT(btxt->data == NULL, ("btxt data already allocated"));
if ((btxt->data = bhnd_nvram_iobuf_copy(src)) == NULL)
return (ENOMEM);
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = 0;
/* Fetch a pointer mapping the entirity of the board text data */
error = bhnd_nvram_io_read_ptr(btxt->data, 0x0, &ptr, io_size, NULL);
if (error)
return (error);
/* Determine the actual size, minus any terminating NUL. We
* parse NUL-terminated C strings, but do not include NUL termination
* in our internal or serialized representations */
str_size = strnlen(ptr, io_size);
/* If the terminating NUL is not found at the end of the buffer,
* this is BCM-RAW or other NUL-delimited NVRAM format. */
if (str_size < io_size && str_size + 1 < io_size)
return (EINVAL);
/* Adjust buffer size to account for NUL termination (if any) */
io_size = str_size;
if ((error = bhnd_nvram_io_setsize(btxt->data, io_size)))
return (error);
/* Process the buffer */
btxt->count = 0;
while (io_offset < io_size) {
const void *envp;
/* Seek to the next key=value entry */
if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset)))
return (error);
/* Determine the entry and line length */
error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset,
&line_len, &env_len);
if (error)
return (error);
/* EOF? */
if (env_len == 0) {
BHND_NV_ASSERT(io_offset == io_size,
("zero-length record returned from "
"bhnd_nvram_btxt_seek_next()"));
break;
}
/* Fetch a pointer to the line start */
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &envp,
env_len, NULL);
if (error)
return (error);
/* Parse the key=value string */
error = bhnd_nvram_parse_env(envp, env_len, '=', &name,
&name_len, &value, &value_len);
if (error) {
return (error);
}
/* Insert a '\0' character, replacing the '=' delimiter and
* allowing us to vend references directly to the variable
* name */
error = bhnd_nvram_io_write(btxt->data, io_offset+name_len,
&(char){'\0'}, 1);
if (error)
return (error);
/* Add to variable count */
btxt->count++;
/* Advance past EOL */
io_offset += line_len;
}
return (0);
}
static int
bhnd_nvram_btxt_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
{
struct bhnd_nvram_btxt *btxt;
int error;
/* Allocate and initialize the BTXT data instance */
btxt = (struct bhnd_nvram_btxt *)nv;
/* Parse the BTXT input data and initialize our backing
* data representation */
if ((error = bhnd_nvram_btxt_init(btxt, io))) {
bhnd_nvram_btxt_free(nv);
return (error);
}
return (0);
}
static void
bhnd_nvram_btxt_free(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
if (btxt->data != NULL)
bhnd_nvram_io_free(btxt->data);
}
size_t
bhnd_nvram_btxt_count(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
return (btxt->count);
}
static int
bhnd_nvram_btxt_size(struct bhnd_nvram_data *nv, size_t *size)
{
struct bhnd_nvram_btxt *btxt = (struct bhnd_nvram_btxt *)nv;
/* The serialized form will be identical in length
* to our backing buffer representation */
*size = bhnd_nvram_io_getsize(btxt->data);
return (0);
}
static int
bhnd_nvram_btxt_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
{
struct bhnd_nvram_btxt *btxt;
size_t limit;
int error;
btxt = (struct bhnd_nvram_btxt *)nv;
limit = *len;
/* Provide actual output size */
if ((error = bhnd_nvram_data_size(nv, len)))
return (error);
if (buf == NULL) {
return (0);
} else if (limit < *len) {
return (ENOMEM);
}
/* Copy our internal representation to the output buffer */
if ((error = bhnd_nvram_io_read(btxt->data, 0x0, buf, *len)))
return (error);
/* Restore the original key=value format, rewriting all '\0'
* key\0value delimiters back to '=' */
for (char *p = buf; (size_t)(p - (char *)buf) < *len; p++) {
if (*p == '\0')
*p = '=';
}
return (0);
}
static uint32_t
bhnd_nvram_btxt_caps(struct bhnd_nvram_data *nv)
{
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
}
static void *
bhnd_nvram_btxt_find(struct bhnd_nvram_data *nv, const char *name)
{
return (bhnd_nvram_data_generic_find(nv, name));
}
static const char *
bhnd_nvram_btxt_next(struct bhnd_nvram_data *nv, void **cookiep)
{
struct bhnd_nvram_btxt *btxt;
const void *nptr;
size_t io_offset, io_size;
int error;
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, *cookiep);
/* Already at EOF? */
if (io_offset == io_size)
return (NULL);
/* Seek to the next entry (if any) */
if ((error = bhnd_nvram_btxt_seek_eol(btxt->data, &io_offset))) {
BHND_NV_LOG("unexpected error in seek_eol(): %d\n", error);
return (NULL);
}
if ((error = bhnd_nvram_btxt_seek_next(btxt->data, &io_offset))) {
BHND_NV_LOG("unexpected error in seek_next(): %d\n", error);
return (NULL);
}
/* Provide the new cookie for this offset */
if (io_offset > UINTPTR_MAX) {
BHND_NV_LOG("io_offset > UINPTR_MAX!\n");
return (NULL);
}
*cookiep = (void *)(uintptr_t)io_offset;
/* Hit EOF? */
if (io_offset == io_size)
return (NULL);
/* Fetch the name pointer; it must be at least 1 byte long */
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &nptr, 1, NULL);
if (error) {
BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
return (NULL);
}
/* Return the name pointer */
return (nptr);
}
static int
bhnd_nvram_btxt_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
{
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
const void *
bhnd_nvram_btxt_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
{
struct bhnd_nvram_btxt *btxt;
const void *eptr;
const char *vptr;
size_t io_offset, io_size;
size_t line_len, env_len;
int error;
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep);
/* At EOF? */
if (io_offset == io_size)
return (NULL);
/* Determine the entry length */
error = bhnd_nvram_btxt_entry_len(btxt->data, io_offset, &line_len,
&env_len);
if (error) {
BHND_NV_LOG("unexpected error in entry_len(): %d\n", error);
return (NULL);
}
/* Fetch the entry's value pointer and length */
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &eptr, env_len,
NULL);
if (error) {
BHND_NV_LOG("unexpected error in read_ptr(): %d\n", error);
return (NULL);
}
error = bhnd_nvram_parse_env(eptr, env_len, '\0', NULL, NULL, &vptr,
len);
if (error) {
BHND_NV_LOG("unexpected error in parse_env(): %d\n", error);
return (NULL);
}
/* Type is always CSTR */
*type = BHND_NVRAM_TYPE_STRING;
return (vptr);
}
static const char *
bhnd_nvram_btxt_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
struct bhnd_nvram_btxt *btxt;
const void *ptr;
size_t io_offset, io_size;
int error;
btxt = (struct bhnd_nvram_btxt *)nv;
io_size = bhnd_nvram_io_getsize(btxt->data);
io_offset = bhnd_nvram_btxt_io_offset(btxt, cookiep);
/* At EOF? */
if (io_offset == io_size)
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
/* Variable name is found directly at the given offset; trailing
* NUL means we can assume that it's at least 1 byte long */
error = bhnd_nvram_io_read_ptr(btxt->data, io_offset, &ptr, 1, NULL);
if (error)
BHND_NV_PANIC("unexpected error in read_ptr(): %d\n", error);
return (ptr);
}
/* Convert cookie back to an I/O offset */
static size_t
bhnd_nvram_btxt_io_offset(struct bhnd_nvram_btxt *btxt, void *cookiep)
{
size_t io_size;
uintptr_t cval;
io_size = bhnd_nvram_io_getsize(btxt->data);
cval = (uintptr_t)cookiep;
BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)"));
BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)"));
return ((size_t)cval);
}
/* Determine the entry length and env 'key=value' string length of the entry
* at @p offset */
static int
bhnd_nvram_btxt_entry_len(struct bhnd_nvram_io *io, size_t offset,
size_t *line_len, size_t *env_len)
{
const uint8_t *baseptr, *p;
const void *rbuf;
size_t nbytes;
int error;
/* Fetch read buffer */
if ((error = bhnd_nvram_io_read_ptr(io, offset, &rbuf, 0, &nbytes)))
return (error);
/* Find record termination (EOL, or '#') */
p = rbuf;
baseptr = rbuf;
while ((size_t)(p - baseptr) < nbytes) {
if (*p == '#' || *p == '\n' || *p == '\r')
break;
p++;
}
/* Got line length, now trim any trailing whitespace to determine
* actual env length */
*line_len = p - baseptr;
*env_len = *line_len;
for (size_t i = 0; i < *line_len; i++) {
char c = baseptr[*line_len - i - 1];
if (!bhnd_nv_isspace(c))
break;
*env_len -= 1;
}
return (0);
}
/* Seek past the next line ending (\r, \r\n, or \n) */
static int
bhnd_nvram_btxt_seek_eol(struct bhnd_nvram_io *io, size_t *offset)
{
const uint8_t *baseptr, *p;
const void *rbuf;
size_t nbytes;
int error;
/* Fetch read buffer */
if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
return (error);
baseptr = rbuf;
p = rbuf;
while ((size_t)(p - baseptr) < nbytes) {
char c = *p;
/* Advance to next char. The next position may be EOF, in which
* case a read will be invalid */
p++;
if (c == '\r') {
/* CR, check for optional LF */
if ((size_t)(p - baseptr) < nbytes) {
if (*p == '\n')
p++;
}
break;
} else if (c == '\n') {
break;
}
}
/* Hit newline or EOF */
*offset += (p - baseptr);
return (0);
}
/* Seek to the next valid non-comment line (or EOF) */
static int
bhnd_nvram_btxt_seek_next(struct bhnd_nvram_io *io, size_t *offset)
{
const uint8_t *baseptr, *p;
const void *rbuf;
size_t nbytes;
int error;
/* Fetch read buffer */
if ((error = bhnd_nvram_io_read_ptr(io, *offset, &rbuf, 0, &nbytes)))
return (error);
/* Skip leading whitespace and comments */
baseptr = rbuf;
p = rbuf;
while ((size_t)(p - baseptr) < nbytes) {
char c = *p;
/* Skip whitespace */
if (bhnd_nv_isspace(c)) {
p++;
continue;
}
/* Skip entire comment line */
if (c == '#') {
size_t line_off = *offset + (p - baseptr);
if ((error = bhnd_nvram_btxt_seek_eol(io, &line_off)))
return (error);
p = baseptr + (line_off - *offset);
continue;
}
/* Non-whitespace, non-comment */
break;
}
*offset += (p - baseptr);
return (0);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
/*-
* 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_SPROMVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_
#ifdef _KERNEL
#include <sys/bitstring.h>
#else
#include <bitstring.h>
#endif
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_io.h"
/** The maximum number of array elements encoded in a single SPROM variable */
#define SPROM_ARRAY_MAXLEN 12
/**
* SPROM opcode per-bind evaluation state.
*/
struct sprom_opcode_bind {
uint8_t count;
uint32_t skip_in; /**< input element skips */
bool skip_in_negative; /**< skip_in should be subtracted */
uint32_t skip_out; /**< output element skip */
};
/**
* SPROM opcode per-variable evaluation state.
*/
struct sprom_opcode_var {
uint8_t nelem; /**< variable array length */
uint32_t mask; /**< current bind input mask */
int8_t shift; /**< current bind input shift */
bhnd_nvram_type base_type; /**< current bind input type */
uint32_t scale; /**< current scale to apply to scaled encodings */
struct sprom_opcode_bind bind; /**< current bind state */
bool have_bind; /**< if bind state is defined */
size_t bind_total; /**< total count of bind operations performed */
};
/**
* SPROM opcode variable definition states.
*
* Ordered to support inequality comparisons
* (e.g. >= SPROM_OPCODE_VAR_STATE_OPEN)
*/
typedef enum {
SPROM_OPCODE_VAR_STATE_NONE = 1, /**< no variable entry available */
SPROM_OPCODE_VAR_STATE_OPEN = 2, /**< currently parsing a variable entry */
SPROM_OPCODE_VAR_STATE_DONE = 3 /**< full variable entry has been parsed */
} sprom_opcode_var_state;
/**
* SPROM opcode evaluation state
*/
struct sprom_opcode_state {
const struct bhnd_sprom_layout *layout; /**< SPROM layout */
/** Current SPROM revision range */
bitstr_t bit_decl(revs, SPROM_OP_REV_MAX);
const uint8_t *input; /**< opcode input position */
/* State preserved across variable definitions */
uint32_t offset; /**< SPROM offset */
size_t vid; /**< Variable ID */
/* State reset after end of each variable definition */
struct sprom_opcode_var var; /**< variable record (if any) */
sprom_opcode_var_state var_state; /**< variable record state */
};
/**
* SPROM opcode variable index entry
*/
struct sprom_opcode_idx {
uint16_t vid; /**< SPROM variable ID */
uint16_t offset; /**< SPROM input offset */
uint16_t opcodes; /**< SPROM opcode offset */
};
/**
* SPROM value storage.
*
* Sufficient for representing the native encoding of any defined SPROM
* variable.
*/
union bhnd_nvram_sprom_storage {
uint8_t u8[SPROM_ARRAY_MAXLEN];
uint16_t u16[SPROM_ARRAY_MAXLEN];
uint32_t u32[SPROM_ARRAY_MAXLEN];
int8_t i8[SPROM_ARRAY_MAXLEN];
int16_t i16[SPROM_ARRAY_MAXLEN];
int32_t i32[SPROM_ARRAY_MAXLEN];
char ch[SPROM_ARRAY_MAXLEN];
};
/**
* SPROM common integer value representation.
*/
union bhnd_nvram_sprom_intv {
uint32_t u32;
int32_t s32;
};
/**
* SPROM data class instance state.
*/
struct bhnd_nvram_sprom {
struct bhnd_nvram_data nv; /**< common instance state */
struct bhnd_nvram_io *data; /**< backing SPROM image */
const struct bhnd_sprom_layout *layout; /**< layout definition */
struct sprom_opcode_state state; /**< opcode eval state */
struct sprom_opcode_idx *idx; /**< opcode index entries */
size_t num_idx; /**< opcode index entry count */
};
#endif /* _BHND_NVRAM_BHND_NVRAM_SPROMVAR_H_ */

View File

@ -0,0 +1,662 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/ctype.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data_tlvreg.h"
/*
* CFE TLV NVRAM data class.
*
* The CFE-defined TLV NVRAM format is used on the WGT634U.
*/
struct bhnd_nvram_tlv {
struct bhnd_nvram_data nv; /**< common instance state */
struct bhnd_nvram_io *data; /**< backing buffer */
size_t count; /**< variable count */
};
BHND_NVRAM_DATA_CLASS_DEFN(tlv, "WGT634U", sizeof(struct bhnd_nvram_tlv))
/** Minimal TLV_ENV record header */
struct bhnd_nvram_tlv_env_hdr {
uint8_t tag;
uint8_t size;
} __packed;
/** Minimal TLV_ENV record */
struct bhnd_nvram_tlv_env {
struct bhnd_nvram_tlv_env_hdr hdr;
uint8_t flags;
char envp[];
} __packed;
/* Return the length in bytes of an TLV_ENV's envp data */
#define NVRAM_TLV_ENVP_DATA_LEN(_env) \
(((_env)->hdr.size < sizeof((_env)->flags)) ? 0 : \
((_env)->hdr.size - sizeof((_env)->flags)))
static int bhnd_nvram_tlv_parse_size(
struct bhnd_nvram_io *io,
size_t *size);
static int bhnd_nvram_tlv_next_record(
struct bhnd_nvram_io *io,
size_t *next, size_t *offset,
uint8_t *tag);
static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_next_env(
struct bhnd_nvram_tlv *tlv,
size_t *next, void **cookiep);
static struct bhnd_nvram_tlv_env *bhnd_nvram_tlv_get_env(
struct bhnd_nvram_tlv *tlv,
void *cookiep);
static void *bhnd_nvram_tlv_to_cookie(
struct bhnd_nvram_tlv *tlv,
size_t io_offset);
static size_t bhnd_nvram_tlv_to_offset(
struct bhnd_nvram_tlv *tlv,
void *cookiep);
static int
bhnd_nvram_tlv_probe(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_tlv_env ident;
size_t nbytes;
int error;
nbytes = bhnd_nvram_io_getsize(io);
/* Handle what might be an empty TLV image */
if (nbytes < sizeof(ident)) {
uint8_t tag;
/* Fetch just the first tag */
error = bhnd_nvram_io_read(io, 0x0, &tag, sizeof(tag));
if (error)
return (error);
/* This *could* be an empty TLV image, but all we're
* testing for here is a single 0x0 byte followed by EOF */
if (tag == NVRAM_TLV_TYPE_END)
return (BHND_NVRAM_DATA_PROBE_MAYBE);
return (ENXIO);
}
/* Otherwise, look at the initial header for a valid TLV ENV tag,
* plus one byte of the entry data */
error = bhnd_nvram_io_read(io, 0x0, &ident,
sizeof(ident) + sizeof(ident.envp[0]));
if (error)
return (error);
/* First entry should be a variable record (which we statically
* assert as being defined to use a single byte size field) */
if (ident.hdr.tag != NVRAM_TLV_TYPE_ENV)
return (ENXIO);
_Static_assert(NVRAM_TLV_TYPE_ENV & NVRAM_TLV_TF_U8_LEN,
"TYPE_ENV is not a U8-sized field");
/* The entry must be at least 3 characters ('x=\0') in length */
if (ident.hdr.size < 3)
return (ENXIO);
/* The first character should be a valid key char (alpha) */
if (!bhnd_nv_isalpha(ident.envp[0]))
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_DEFAULT);
}
/**
* Initialize @p tlv with the provided NVRAM TLV data mapped by @p src.
*
* @param tlv A newly allocated data instance.
*/
static int
bhnd_nvram_tlv_init(struct bhnd_nvram_tlv *tlv, struct bhnd_nvram_io *src)
{
struct bhnd_nvram_tlv_env *env;
size_t size;
size_t next;
int error;
BHND_NV_ASSERT(tlv->data == NULL, ("tlv data already initialized"));
/* Determine the actual size of the TLV source data */
if ((error = bhnd_nvram_tlv_parse_size(src, &size)))
return (error);
/* Copy to our own internal buffer */
if ((tlv->data = bhnd_nvram_iobuf_copy_range(src, 0x0, size)) == NULL)
return (ENOMEM);
/* Initialize our backing buffer */
tlv->count = 0;
next = 0;
while ((env = bhnd_nvram_tlv_next_env(tlv, &next, NULL)) != NULL) {
size_t env_len;
size_t name_len;
/* TLV_ENV data must not be empty */
env_len = NVRAM_TLV_ENVP_DATA_LEN(env);
if (env_len == 0) {
BHND_NV_LOG("cannot parse zero-length TLV_ENV record "
"data\n");
return (EINVAL);
}
/* Parse the key=value string, and then replace the '='
* delimiter with '\0' to allow us to provide direct
* name pointers from our backing buffer */
error = bhnd_nvram_parse_env(env->envp, env_len, '=', NULL,
&name_len, NULL, NULL);
if (error) {
BHND_NV_LOG("error parsing TLV_ENV data: %d\n", error);
return (error);
}
/* Replace '=' with '\0' */
*(env->envp + name_len) = '\0';
/* Add to variable count */
tlv->count++;
};
return (0);
}
static int
bhnd_nvram_tlv_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
{
struct bhnd_nvram_tlv *tlv;
int error;
/* Allocate and initialize the TLV data instance */
tlv = (struct bhnd_nvram_tlv *)nv;
/* Parse the TLV input data and initialize our backing
* data representation */
if ((error = bhnd_nvram_tlv_init(tlv, io))) {
bhnd_nvram_tlv_free(nv);
return (error);
}
return (0);
}
static void
bhnd_nvram_tlv_free(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
if (tlv->data != NULL)
bhnd_nvram_io_free(tlv->data);
}
size_t
bhnd_nvram_tlv_count(struct bhnd_nvram_data *nv)
{
struct bhnd_nvram_tlv *tlv = (struct bhnd_nvram_tlv *)nv;
return (tlv->count);
}
static int
bhnd_nvram_tlv_size(struct bhnd_nvram_data *nv, size_t *size)
{
/* Let the serialization implementation calculate the length */
return (bhnd_nvram_data_serialize(nv, NULL, size));
}
static int
bhnd_nvram_tlv_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
{
struct bhnd_nvram_tlv *tlv;
size_t limit;
size_t next;
uint8_t tag;
int error;
tlv = (struct bhnd_nvram_tlv *)nv;
/* Save the buffer capacity */
if (buf == NULL)
limit = 0;
else
limit = *len;
/* Write all of our TLV records to the output buffer (or just
* calculate the buffer size that would be required) */
next = 0;
do {
struct bhnd_nvram_tlv_env *env;
uint8_t *p;
size_t name_len;
size_t rec_offset, rec_size;
/* Parse the TLV record */
error = bhnd_nvram_tlv_next_record(tlv->data, &next,
&rec_offset, &tag);
if (error)
return (error);
rec_size = next - rec_offset;
/* Calculate our output pointer */
if (rec_offset > limit || limit - rec_offset < rec_size) {
/* buffer is full; cannot write */
p = NULL;
} else {
p = (uint8_t *)buf + rec_offset;
}
/* If not writing, nothing further to do for this record */
if (p == NULL)
continue;
/* Copy to the output buffer */
error = bhnd_nvram_io_read(tlv->data, rec_offset, p, rec_size);
if (error)
return (error);
/* All further processing is TLV_ENV-specific */
if (tag != NVRAM_TLV_TYPE_ENV)
continue;
/* Restore the original key=value format, rewriting '\0'
* delimiter back to '=' */
env = (struct bhnd_nvram_tlv_env *)p;
name_len = strlen(env->envp); /* skip variable name */
*(env->envp + name_len) = '='; /* set '=' */
} while (tag != NVRAM_TLV_TYPE_END);
/* The 'next' offset should now point at EOF, and represents
* the total length of the serialized output. */
*len = next;
if (buf != NULL && limit < *len)
return (ENOMEM);
return (0);
}
static uint32_t
bhnd_nvram_tlv_caps(struct bhnd_nvram_data *nv)
{
return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
}
static const char *
bhnd_nvram_tlv_next(struct bhnd_nvram_data *nv, void **cookiep)
{
struct bhnd_nvram_tlv *tlv;
struct bhnd_nvram_tlv_env *env;
size_t io_offset;
tlv = (struct bhnd_nvram_tlv *)nv;
/* Seek past the TLV_ENV record referenced by cookiep */
io_offset = bhnd_nvram_tlv_to_offset(tlv, *cookiep);
if (bhnd_nvram_tlv_next_env(tlv, &io_offset, NULL) == NULL)
BHND_NV_PANIC("invalid cookiep: %p\n", cookiep);
/* Fetch the next TLV_ENV record */
if ((env = bhnd_nvram_tlv_next_env(tlv, &io_offset, cookiep)) == NULL) {
/* No remaining ENV records */
return (NULL);
}
/* Return the NUL terminated name */
return (env->envp);
}
static void *
bhnd_nvram_tlv_find(struct bhnd_nvram_data *nv, const char *name)
{
return (bhnd_nvram_data_generic_find(nv, name));
}
static int
bhnd_nvram_tlv_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
size_t *len, bhnd_nvram_type type)
{
return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
}
static const void *
bhnd_nvram_tlv_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type)
{
struct bhnd_nvram_tlv *tlv;
struct bhnd_nvram_tlv_env *env;
const char *val;
int error;
tlv = (struct bhnd_nvram_tlv *)nv;
/* Fetch pointer to the TLV_ENV record */
if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
/* Parse value pointer and length from key\0value data */
error = bhnd_nvram_parse_env(env->envp, NVRAM_TLV_ENVP_DATA_LEN(env),
'\0', NULL, NULL, &val, len);
if (error)
BHND_NV_PANIC("unexpected error parsing '%s'", env->envp);
/* Type is always CSTR */
*type = BHND_NVRAM_TYPE_STRING;
return (val);
}
static const char *
bhnd_nvram_tlv_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
struct bhnd_nvram_tlv *tlv;
const struct bhnd_nvram_tlv_env *env;
tlv = (struct bhnd_nvram_tlv *)nv;
/* Fetch pointer to the TLV_ENV record */
if ((env = bhnd_nvram_tlv_get_env(tlv, cookiep)) == NULL)
BHND_NV_PANIC("invalid cookiep: %p", cookiep);
/* Return name pointer */
return (&env->envp[0]);
}
/**
* Iterate over the records starting at @p next, returning the parsed
* record's @p tag, @p size, and @p offset.
*
* @param io The I/O context to parse.
* @param[in,out] next The next offset to be parsed, or 0x0
* to begin parsing. Upon successful
* return, will be set to the offset of the
* next record (or EOF, if
* NVRAM_TLV_TYPE_END was parsed).
* @param[out] offset The record's value offset.
* @param[out] tag The record's tag.
*
* @retval 0 success
* @retval EINVAL if parsing @p io as TLV fails.
* @retval non-zero if reading @p io otherwise fails, a regular unix error
* code will be returned.
*/
static int
bhnd_nvram_tlv_next_record(struct bhnd_nvram_io *io, size_t *next, size_t
*offset, uint8_t *tag)
{
size_t io_offset, io_size;
uint16_t parsed_len;
uint8_t len_hdr[2];
int error;
io_offset = *next;
io_size = bhnd_nvram_io_getsize(io);
/* Save the record offset */
if (offset != NULL)
*offset = io_offset;
/* Fetch initial tag */
error = bhnd_nvram_io_read(io, io_offset, tag, sizeof(*tag));
if (error)
return (error);
io_offset++;
/* EOF */
if (*tag == NVRAM_TLV_TYPE_END) {
*next = io_offset;
return (0);
}
/* Read length field */
if (*tag & NVRAM_TLV_TF_U8_LEN) {
error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
sizeof(len_hdr[0]));
if (error) {
BHND_NV_LOG("error reading TLV record size: %d\n",
error);
return (error);
}
parsed_len = len_hdr[0];
io_offset++;
} else {
error = bhnd_nvram_io_read(io, io_offset, &len_hdr,
sizeof(len_hdr));
if (error) {
BHND_NV_LOG("error reading 16-bit TLV record "
"size: %d\n", error);
return (error);
}
parsed_len = (len_hdr[0] << 8) | len_hdr[1];
io_offset += 2;
}
/* Advance to next record */
if (parsed_len > io_size || io_size - parsed_len < io_offset) {
/* Hit early EOF */
BHND_NV_LOG("TLV record length %hu truncated by input "
"size of %zu\n", parsed_len, io_size);
return (EINVAL);
}
*next = io_offset + parsed_len;
/* Valid record found */
return (0);
}
/**
* Parse the TLV data in @p io to determine the total size of the TLV
* data mapped by @p io (which may be less than the size of @p io).
*/
static int
bhnd_nvram_tlv_parse_size(struct bhnd_nvram_io *io, size_t *size)
{
size_t next;
uint8_t tag;
int error;
/* We have to perform a minimal parse to determine the actual length */
next = 0x0;
*size = 0x0;
/* Iterate over the input until we hit END tag or the read fails */
do {
error = bhnd_nvram_tlv_next_record(io, &next, NULL, &tag);
if (error)
return (error);
} while (tag != NVRAM_TLV_TYPE_END);
/* Offset should now point to EOF */
BHND_NV_ASSERT(next <= bhnd_nvram_io_getsize(io),
("parse returned invalid EOF offset"));
*size = next;
return (0);
}
/**
* Iterate over the records in @p tlv, returning a pointer to the next
* NVRAM_TLV_TYPE_ENV record, or NULL if EOF is reached.
*
* @param tlv The TLV instance.
* @param[in,out] next The next offset to be parsed, or 0x0
* to begin parsing. Upon successful
* return, will be set to the offset of the
* next record.
*/
static struct bhnd_nvram_tlv_env *
bhnd_nvram_tlv_next_env(struct bhnd_nvram_tlv *tlv, size_t *next,
void **cookiep)
{
uint8_t tag;
int error;
/* Find the next TLV_ENV record, starting at @p next */
do {
void *c;
size_t offset;
/* Fetch the next TLV record */
error = bhnd_nvram_tlv_next_record(tlv->data, next, &offset,
&tag);
if (error) {
BHND_NV_LOG("unexpected error in next_record(): %d\n",
error);
return (NULL);
}
/* Only interested in ENV records */
if (tag != NVRAM_TLV_TYPE_ENV)
continue;
/* Map and return TLV_ENV record pointer */
c = bhnd_nvram_tlv_to_cookie(tlv, offset);
/* Provide the cookiep value for the returned record */
if (cookiep != NULL)
*cookiep = c;
return (bhnd_nvram_tlv_get_env(tlv, c));
} while (tag != NVRAM_TLV_TYPE_END);
/* No remaining ENV records */
return (NULL);
}
/**
* Return a pointer to the TLV_ENV record for @p cookiep, or NULL
* if none vailable.
*/
static struct bhnd_nvram_tlv_env *
bhnd_nvram_tlv_get_env(struct bhnd_nvram_tlv *tlv, void *cookiep)
{
struct bhnd_nvram_tlv_env *env;
void *ptr;
size_t navail;
size_t io_offset, io_size;
int error;
io_size = bhnd_nvram_io_getsize(tlv->data);
io_offset = bhnd_nvram_tlv_to_offset(tlv, cookiep);
/* At EOF? */
if (io_offset == io_size)
return (NULL);
/* Fetch non-const pointer to the record entry */
error = bhnd_nvram_io_write_ptr(tlv->data, io_offset, &ptr,
sizeof(env->hdr), &navail);
if (error) {
/* Should never occur with a valid cookiep */
BHND_NV_LOG("error mapping record for cookiep: %d\n", error);
return (NULL);
}
/* Validate the record pointer */
env = ptr;
if (env->hdr.tag != NVRAM_TLV_TYPE_ENV) {
/* Should never occur with a valid cookiep */
BHND_NV_LOG("non-ENV record mapped for %p\n", cookiep);
return (NULL);
}
/* Is the required variable name data is mapped? */
if (navail < sizeof(struct bhnd_nvram_tlv_env_hdr) + env->hdr.size ||
env->hdr.size == sizeof(env->flags))
{
/* Should never occur with a valid cookiep */
BHND_NV_LOG("TLV_ENV variable data not mapped for %p\n",
cookiep);
return (NULL);
}
return (env);
}
/**
* Return a cookiep for the given I/O offset.
*/
static void *
bhnd_nvram_tlv_to_cookie(struct bhnd_nvram_tlv *tlv, size_t io_offset)
{
BHND_NV_ASSERT(io_offset < bhnd_nvram_io_getsize(tlv->data),
("io_offset %zu out-of-range", io_offset));
BHND_NV_ASSERT(io_offset < UINTPTR_MAX,
("io_offset %#zx exceeds UINTPTR_MAX", io_offset));
return ((void *)(uintptr_t)(io_offset));
}
/* Convert a cookiep back to an I/O offset */
static size_t
bhnd_nvram_tlv_to_offset(struct bhnd_nvram_tlv *tlv, void *cookiep)
{
size_t io_size;
uintptr_t cval;
io_size = bhnd_nvram_io_getsize(tlv->data);
cval = (uintptr_t)cookiep;
BHND_NV_ASSERT(cval < SIZE_MAX, ("cookie > SIZE_MAX)"));
BHND_NV_ASSERT(cval <= io_size, ("cookie > io_size)"));
return ((size_t)cval);
}

View File

@ -29,47 +29,12 @@
* $FreeBSD$ * $FreeBSD$
*/ */
#ifndef _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ #ifndef _BHND_NVRAM_BHND_NVRAM_TLVREG_H_
#define _BHND_NVRAM_BHND_NVRAM_PARSERREG_H_ #define _BHND_NVRAM_BHND_NVRAM_TLVREG_H_
#define NVRAM_GET_BITS(_value, _field) \
((_value & _field ## _MASK) >> _field ## _SHIFT)
/* NVRAM header fields */
#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
#define NVRAM_VERSION 1
#define NVRAM_CRC_SKIP 9 /* skip magic, size, and crc8 */
#define NVRAM_CFG0_CRC_MASK 0x000000FF
#define NVRAM_CFG0_CRC_SHIFT 0
#define NVRAM_CFG0_VER_MASK 0x0000FF00
#define NVRAM_CFG0_VER_SHIFT 8
#define NVRAM_CFG0_SDRAM_INIT_MASK 0xFFFF0000
#define NVRAM_CFG0_SDRAM_INIT_SHIFT 16
#define NVRAM_CFG0_SDRAM_INIT_VAR "sdram_init"
#define NVRAM_CFG0_SDRAM_INIT_FMT "0x%04x"
#define NVRAM_CFG1_SDRAM_CFG_MASK 0x0000FFFF
#define NVRAM_CFG1_SDRAM_CFG_SHIFT 0
#define NVRAM_CFG1_SDRAM_CFG_VAR "sdram_config"
#define NVRAM_CFG1_SDRAM_CFG_FMT "0x%04x"
#define NVRAM_CFG1_SDRAM_REFRESH_MASK 0xFFFF0000
#define NVRAM_CFG1_SDRAM_REFRESH_SHIFT 16
#define NVRAM_CFG1_SDRAM_REFRESH_VAR "sdram_refresh"
#define NVRAM_CFG1_SDRAM_REFRESH_FMT "0x%04x"
#define NVRAM_SDRAM_NCDL_MASK UINT32_MAX
#define NVRAM_SDRAM_NCDL_SHIFT 0
#define NVRAM_SDRAM_NCDL_VAR "sdram_ncdl"
#define NVRAM_SDRAM_NCDL_FMT "0x%08x"
/* WGT634U-specific TLV encoding */ /* WGT634U-specific TLV encoding */
#define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */ #define NVRAM_TLV_TF_U8_LEN 0x01 /**< type has 8-bit length */
#define NVRAM_TLV_TYPE_END 0x00 /**< end of table */ #define NVRAM_TLV_TYPE_END 0x00 /**< end of table */
#define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */ #define NVRAM_TLV_TYPE_ENV 0x01 /**< variable record */
#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */ #endif /* _BHND_NVRAM_BHND_NVRAM_TLVREG_H_ */

View File

@ -0,0 +1,188 @@
/*-
* Copyright (c) 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_DATAVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_DATAVAR_H_
#include <sys/param.h>
#include <sys/linker_set.h>
#include <sys/refcount.h>
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_data.h"
/** Registered NVRAM parser class instances. */
SET_DECLARE(bhnd_nvram_data_class_set, bhnd_nvram_data_class_t);
void *bhnd_nvram_data_generic_find(struct bhnd_nvram_data *nv,
const char *name);
int bhnd_nvram_data_generic_rp_getvar(struct bhnd_nvram_data *nv,
void *cookiep, void *outp, size_t *olen, bhnd_nvram_type otype);
/** @see bhnd_nvram_data_class_desc() */
typedef const char *(bhnd_nvram_data_op_class_desc)(void);
/** @see bhnd_nvram_data_probe() */
typedef int (bhnd_nvram_data_op_probe)(struct bhnd_nvram_io *io);
/** @see bhnd_nvram_data_new() */
typedef int (bhnd_nvram_data_op_new)(struct bhnd_nvram_data *nv,
struct bhnd_nvram_io *io);
/** Free all resources associated with @p nv. Called by
* bhnd_nvram_data_release() when the reference count reaches zero. */
typedef void (bhnd_nvram_data_op_free)(struct bhnd_nvram_data *nv);
/** @see bhnd_nvram_data_count() */
typedef size_t (bhnd_nvram_data_op_count)(struct bhnd_nvram_data *nv);
/** @see bhnd_nvram_data_size() */
typedef int (bhnd_nvram_data_op_size)(struct bhnd_nvram_data *nv,
size_t *len);
/** @see bhnd_nvram_data_serialize() */
typedef int (bhnd_nvram_data_op_serialize)(
struct bhnd_nvram_data *nv, void *buf,
size_t *len);
/** @see bhnd_nvram_data_caps() */
typedef uint32_t (bhnd_nvram_data_op_caps)(struct bhnd_nvram_data *nv);
/** @see bhnd_nvram_data_next() */
typedef const char *(bhnd_nvram_data_op_next)(struct bhnd_nvram_data *nv,
void **cookiep);
/** @see bhnd_nvram_data_find() */
typedef void *(bhnd_nvram_data_op_find)(struct bhnd_nvram_data *nv,
const char *name);
/** @see bhnd_nvram_data_getvar_name() */
typedef const char *(bhnd_nvram_data_op_getvar_name)(
struct bhnd_nvram_data *nv, void *cookiep);
/** @see bhnd_nvram_data_getvar() */
typedef int (bhnd_nvram_data_op_getvar)(struct bhnd_nvram_data *nv,
void *cookiep, void *buf, size_t *len,
bhnd_nvram_type type);
/** @see bhnd_nvram_data_getvar_ptr() */
typedef const void *(bhnd_nvram_data_op_getvar_ptr)(
struct bhnd_nvram_data *nv, void *cookiep,
size_t *len, bhnd_nvram_type *type);
/**
* NVRAM data class.
*/
struct bhnd_nvram_data_class {
const char *desc; /**< description */
size_t size; /**< instance size */
bhnd_nvram_data_op_probe *op_probe;
bhnd_nvram_data_op_new *op_new;
bhnd_nvram_data_op_free *op_free;
bhnd_nvram_data_op_count *op_count;
bhnd_nvram_data_op_size *op_size;
bhnd_nvram_data_op_serialize *op_serialize;
bhnd_nvram_data_op_caps *op_caps;
bhnd_nvram_data_op_next *op_next;
bhnd_nvram_data_op_find *op_find;
bhnd_nvram_data_op_getvar *op_getvar;
bhnd_nvram_data_op_getvar_ptr *op_getvar_ptr;
bhnd_nvram_data_op_getvar_name *op_getvar_name;
};
/**
* NVRAM data instance.
*/
struct bhnd_nvram_data {
struct bhnd_nvram_data_class *cls;
volatile u_int refs;
};
/*
* Helper macro for BHND_NVRAM_DATA_CLASS_DEFN().
*
* Declares a bhnd_nvram_data_class method implementation with class name
* _cname and method name _mname
*/
#define BHND_NVRAM_DATA_CLASS_DECL_METHOD(_cname, _mname) \
static bhnd_nvram_data_op_ ## _mname \
bhnd_nvram_ ## _cname ## _ ## _mname; \
/*
* Helper macro for BHND_NVRAM_DATA_CLASS_DEFN().
*
* Assign a bhnd_nvram_data_class method implementation with class name
* @p _cname and method name @p _mname
*/
#define BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD(_cname, _mname) \
.op_ ## _mname = bhnd_nvram_ ## _cname ## _ ## _mname,
/*
* Helper macro for BHND_NVRAM_DATA_CLASS_DEFN().
*
* Iterate over all bhnd_nvram_data_class method names, calling
* _macro with the class name _cname as the first argument, and
* a bhnd_nvram_data_class method name as the second.
*/
#define BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, _macro) \
_macro(_cname, probe) \
_macro(_cname, new) \
_macro(_cname, free) \
_macro(_cname, count) \
_macro(_cname, size) \
_macro(_cname, serialize) \
_macro(_cname, caps) \
_macro(_cname, next) \
_macro(_cname, find) \
_macro(_cname, getvar) \
_macro(_cname, getvar_ptr) \
_macro(_cname, getvar_name)
/**
* Define a bhnd_nvram_data_class with class name @p _n and description
* @p _desc, and register with bhnd_nvram_data_class_set.
*/
#define BHND_NVRAM_DATA_CLASS_DEFN(_cname, _desc, _size) \
BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \
BHND_NVRAM_DATA_CLASS_DECL_METHOD) \
\
struct bhnd_nvram_data_class bhnd_nvram_## _cname ## _class = { \
.desc = (_desc), \
.size = (_size), \
BHND_NVRAM_DATA_CLASS_ITER_METHODS(_cname, \
BHND_NVRAM_DATA_CLASS_ASSIGN_METHOD) \
}; \
\
DATA_SET(bhnd_nvram_data_class_set, \
bhnd_nvram_## _cname ## _class);
#endif /* _BHND_NVRAM_BHND_NVRAM_DATAVAR_H_ */

View File

@ -0,0 +1,205 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#endif /* _KERNEL */
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_iovar.h"
/**
* Read exactly @p nbytes from @p io at @p offset.
*
* @param io NVRAM I/O context.
* @param offset The offset within @p io at which to perform the read.
* @param[out] buffer Output buffer to which @p nbytes from @p io will be
* written.
* @param nbytes The maximum number of bytes to be read from @p io.
*
* @retval 0 success
* @retval EIO if an input error occured reading @p io.
* @retval ENXIO if the request for @p offset or @p nbytes exceeds the size
* of @p io.
* @retval EFAULT if @p io requires I/O request alignment and @p offset is
* misaligned.
* @retval EFAULT if @p io requires I/O request alignment and @p nbytes is
* misaligned and cannot be rounded down to an aligned non-zero value.
*/
int
bhnd_nvram_io_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
return (io->iops->read(io, offset, buffer, nbytes));
}
/**
* Attempt to fetch a pointer to @p io's internal read buffer, if
* supported by @p io.
*
* The returned pointer is only gauranteed to be valid until the next I/O
* operation performed on @p io; concrete implementations of bhnd_nvram_io
* may provide stronger gaurantees.
*
* @param io NVRAM I/O context.
* @param offset The offset within @p io for which to return a buffer pointer.
* @param[out] ptr On success, will be initialized with a pointer to @p io's
* internal read buffer.
* @param nbytes The minimum number of bytes that must be readable at @p offset.
* @param[out] navail The actual number of readable bytes, which may be greater
* than @p nbytes. If this value is not required, a NULL pointer may be
* provided.
*
* @retval 0 success
* @retval EIO if an input error occured reading @p io.
* @retval ENODEV if @p io does not support direct access to its backing read
* buffer.
* @retval ENXIO if the request exceeds the size of @p io.
* @retval EFAULT if @p io requires I/O request alignment and @p offset or
* @p nbytes are misaligned.
*/
int
bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail)
{
return (io->iops->read_ptr(io, offset, ptr, nbytes, navail));
}
/**
* Write @p nbytes to @p io at @p offset.
*
* @param io NVRAM I/O context.
* @param offset The offset within @p io at which to perform the write.
* @param buffer Data to be written to @p io.
* @param nbytes The number of bytes to be written from @p buffer.
*
* @retval 0 success
* @retval EIO if an output error occurs writing to @p io.
* @retval ENODEV if @p io does not support writing.
* @retval ENXIO if @p io does not support writes beyond the existing
* end-of-file, and a write at @p offset would exceed the size of the @p io
* backing data store.
* @retval EFAULT if @p io requires I/O request alignment and @p offset or
* @p nbytes are misaligned.
*/
int
bhnd_nvram_io_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
return (io->iops->write(io, offset, buffer, nbytes));
}
/**
* Attempt to fetch a writable pointer to @p io's internal write buffer, if
* supported by @p io.
*
* The returned pointer is only gauranteed to be valid until the next I/O
* operation performed on @p io; concrete implementations of bhnd_nvram_io
* may provide stronger gaurantees.
*
* @param io NVRAM I/O context.
* @param offset The offset within @p io for which to return a buffer pointer.
* @param[in,out] ptr On success, will be initialized with a pointer to @p io's
* internal buffer at which up to @p nbytes may be written.
* @param nbytes The minimum number of bytes that must be writable at @p offset.
* @param[out] navail The actual number of writable bytes, which may be greater
* than @p nbytes. If this value is not required, a NULL pointer may be
* provided.
*
* @retval 0 success
* @retval EIO if an output error occurs preparing @p io's write buffer.
* @retval ENODEV if @p io does not support direct access to its backing write
* buffer.
* @retval ENXIO if @p io does not support writes beyond the existing
* end-of-file, and a write at @p offset of @p nbytes would exceed the size of
* the @p io backing data store.
* @retval EFAULT if @p io requires I/O request alignment and @p offset or
* @p nbytes are misaligned.
*/
int
bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io, size_t offset, void **ptr,
size_t nbytes, size_t *navail)
{
return (io->iops->write_ptr(io, offset, ptr, nbytes, navail));
}
/**
* Return the total number of bytes readable via @p io.
*
* @param io NVRAM I/O context.
*/
size_t
bhnd_nvram_io_getsize(struct bhnd_nvram_io *io)
{
return (io->iops->getsize(io));
}
/**
* Attempt to set the size of @p io to @p size.
*
* If the total size of @p io is increased, the contents of the newly mapped
* bytes are undefined; concrete implementations of bhnd_nvram_io may
* provide stronger gaurantees.
*
* @param io NVRAM I/O context.
* @param size The new size.
*
* @retval 0 success
* @retval EIO if an I/O error occurs resizing @p io.
* @retval ENODEV if @p io does not support resizing.
* @retval ENXIO if @p size exceeds the capacity or other limits of @p io.
* @retval EFAULT if @p io requires I/O request alignment and @p size is
* misaligned.
*/
int
bhnd_nvram_io_setsize(struct bhnd_nvram_io *io, size_t size)
{
return (io->iops->setsize(io, size));
}
/**
* Free a previously allocated I/O context, releasing all associated
* resources.
*
* @param io The I/O context to be freed.
*/
void
bhnd_nvram_io_free(struct bhnd_nvram_io *io)
{
return (io->iops->free(io));
}

View File

@ -0,0 +1,79 @@
/*-
* Copyright (c) 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_IO_H_
#define _BHND_NVRAM_BHND_NVRAM_IO_H_
#ifdef _KERNEL
#include <sys/param.h>
#include <dev/bhnd/bhnd.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#endif /* _KERNEL */
struct bhnd_nvram_io;
struct bhnd_nvram_io *bhnd_nvram_iobuf_new(const void *buffer, size_t size);
struct bhnd_nvram_io *bhnd_nvram_iobuf_empty(size_t size, size_t capacity);
struct bhnd_nvram_io *bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src);
struct bhnd_nvram_io *bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src,
size_t offset, size_t size);
#ifdef _KERNEL
struct bhnd_nvram_io *bhnd_nvram_iores_new(struct bhnd_resource *r,
bus_size_t offset, bus_size_t size,
u_int bus_width);
#endif /* _KERNEL */
size_t bhnd_nvram_io_getsize(struct bhnd_nvram_io *io);
int bhnd_nvram_io_setsize(struct bhnd_nvram_io *io,
size_t size);
int bhnd_nvram_io_read(struct bhnd_nvram_io *io,
size_t offset, void *buffer, size_t nbytes);
int bhnd_nvram_io_read_ptr(struct bhnd_nvram_io *io,
size_t offset, const void **ptr, size_t nbytes,
size_t *navail);
int bhnd_nvram_io_write(struct bhnd_nvram_io *io,
size_t offset, void *buffer, size_t nbytes);
int bhnd_nvram_io_write_ptr(struct bhnd_nvram_io *io,
size_t offset, void **ptr, size_t nbytes,
size_t *navail);
void bhnd_nvram_io_free(struct bhnd_nvram_io *io);
#endif /* _BHND_NVRAM_BHND_NVRAM_IO_H_ */

View File

@ -0,0 +1,341 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_iovar.h"
/**
* Buffer-backed NVRAM I/O context.
*
* iobuf instances are gauranteed to provide persistent references to its
* backing contigious buffer via bhnd_nvram_io_read_ptr() and
* bhnd_nvram_io_write_ptr().
*/
struct bhnd_nvram_iobuf {
struct bhnd_nvram_io io; /**< common I/O instance state */
void *buf; /**< backing buffer. if inline-allocated, will
be a reference to data[]. */
size_t size; /**< size of @p buf */
size_t capacity; /**< capacity of @p buf */
uint8_t data[]; /**< inline buffer allocation */
};
BHND_NVRAM_IOPS_DEFN(iobuf)
/**
* Allocate and return a new I/O context with an uninitialized
* buffer of @p size and @p capacity.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* If @p capacity is less than @p size, a capacity of @p size will be used.
*
* @param size The initial size of the I/O context.
* @param capacity The total capacity of the I/O context buffer;
* the returned I/O context may be resized up to
* @p capacity via bhnd_nvram_io_setsize().
*
* @retval bhnd_nvram_iobuf success.
* @retval NULL allocation failed.
* @retval NULL the requested @p capacity is less than
* @p size.
*/
struct bhnd_nvram_io *
bhnd_nvram_iobuf_empty(size_t size, size_t capacity)
{
struct bhnd_nvram_iobuf *iobuf;
size_t iosz;
bool inline_alloc;
/* Sanity check the capacity */
if (size > capacity)
return (NULL);
/* Would sizeof(iobuf)+capacity overflow? */
if (SIZE_MAX - sizeof(*iobuf) < capacity) {
inline_alloc = false;
iosz = sizeof(*iobuf);
} else {
inline_alloc = true;
iosz = sizeof(*iobuf) + capacity;
}
/* Allocate I/O context */
iobuf = bhnd_nv_malloc(iosz);
if (iobuf == NULL)
return (NULL);
iobuf->io.iops = &bhnd_nvram_iobuf_ops;
iobuf->buf = NULL;
iobuf->size = size;
iobuf->capacity = capacity;
/* Either allocate our backing buffer, or initialize the
* backing buffer with a reference to our inline allocation. */
if (inline_alloc)
iobuf->buf = &iobuf->data;
else
iobuf->buf = bhnd_nv_malloc(iobuf->capacity);
if (iobuf->buf == NULL) {
bhnd_nv_free(iobuf);
return (NULL);
}
return (&iobuf->io);
}
/**
* Allocate and return a new I/O context, copying @p size from @p buffer.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* @param buffer The buffer data be copied by the returned I/O context.
* @param size The size of @p buffer, in bytes.
*
* @retval bhnd_nvram_io success.
* @retval NULL allocation failed.
*/
struct bhnd_nvram_io *
bhnd_nvram_iobuf_new(const void *buffer, size_t size)
{
struct bhnd_nvram_io *io;
struct bhnd_nvram_iobuf *iobuf;
/* Allocate the iobuf */
if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL)
return (NULL);
/* Copy the input to our new iobuf instance */
iobuf = (struct bhnd_nvram_iobuf *)io;
memcpy(iobuf->buf, buffer, iobuf->size);
return (io);
}
/**
* Allocate and return a new I/O context providing an in-memory copy
* of the data mapped by @p src.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* @param src The I/O context to be copied.
*
* @retval bhnd_nvram_io success.
* @retval NULL allocation failed.
* @retval NULL copying @p src failed.
*/
struct bhnd_nvram_io *
bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src)
{
return (bhnd_nvram_iobuf_copy_range(src, 0x0,
bhnd_nvram_io_getsize(src)));
}
/**
* Allocate and return a new I/O context providing an in-memory copy
* of @p size bytes mapped at @p offset by @p src.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* @param src The I/O context to be copied.
* @param offset The offset of the bytes to be copied from @p src.
* @param size The number of bytes to copy at @p offset from @p src.
*
* @retval bhnd_nvram_io success.
* @retval NULL allocation failed.
* @retval NULL copying @p src failed.
*/
struct bhnd_nvram_io *
bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset,
size_t size)
{
struct bhnd_nvram_io *io;
struct bhnd_nvram_iobuf *iobuf;
int error;
/* Check if offset+size would overflow */
if (SIZE_MAX - size < offset)
return (NULL);
/* Allocate the iobuf instance */
if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL)
return (NULL);
/* Copy the input I/O context */
iobuf = (struct bhnd_nvram_iobuf *)io;
if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) {
bhnd_nvram_io_free(&iobuf->io);
return (NULL);
}
return (io);
}
static void
bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;
/* Free the backing buffer if it wasn't allocated inline */
if (iobuf->buf != &iobuf->data)
bhnd_nv_free(iobuf->buf);
bhnd_nv_free(iobuf);
}
static size_t
bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;
return (iobuf->size);
}
static int
bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size)
{
struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;
/* Can't exceed the actual capacity */
if (size > iobuf->capacity)
return (ENXIO);
iobuf->size = size;
return (0);
}
/* Common iobuf_(read|write)_ptr implementation */
static int
bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr,
size_t nbytes, size_t *navail)
{
size_t avail;
/* Verify offset+nbytes fall within the buffer range */
if (offset > iobuf->size)
return (ENXIO);
avail = iobuf->size - offset;
if (avail < nbytes)
return (ENXIO);
/* Valid I/O range, provide a pointer to the buffer and the
* total count of available bytes */
*ptr = ((uint8_t *)iobuf->buf) + offset;
if (navail != NULL)
*navail = avail;
return (0);
}
static int
bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail)
{
struct bhnd_nvram_iobuf *iobuf;
void *ioptr;
int error;
iobuf = (struct bhnd_nvram_iobuf *) io;
/* Return a pointer into our backing buffer */
error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail);
if (error)
return (error);
*ptr = ioptr;
return (0);
}
static int
bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset,
void **ptr, size_t nbytes, size_t *navail)
{
struct bhnd_nvram_iobuf *iobuf;
iobuf = (struct bhnd_nvram_iobuf *) io;
/* Return a pointer into our backing buffer */
return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail));
}
static int
bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
const void *ptr;
int error;
/* Try to fetch a direct pointer for at least nbytes */
if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL)))
return (error);
/* Copy out the requested data */
memcpy(buffer, ptr, nbytes);
return (0);
}
static int
bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset,
void *buffer, size_t nbytes)
{
void *ptr;
int error;
/* Try to fetch a direct pointer for at least nbytes */
if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL)))
return (error);
/* Copy in the provided data */
memcpy(ptr, buffer, nbytes);
return (0);
}

View File

@ -0,0 +1,302 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/bhnd/bhnd.h>
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_iovar.h"
/**
* BHND resource-backed NVRAM I/O context.
*/
struct bhnd_nvram_iores {
struct bhnd_nvram_io io; /**< common I/O instance state */
struct bhnd_resource *res; /**< backing resource (borrowed ref) */
size_t offset; /**< offset within res */
size_t size; /**< size relative to the base offset */
u_int bus_width; /**< data type byte width to be used
when performing bus operations
on res. (1, 2, or 4 bytes) */
};
BHND_NVRAM_IOPS_DEFN(iores);
/**
* Allocate and return a new I/O context backed by a borrowed reference to @p r.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* @param r The resource to be mapped by the returned I/O
* context.
* @param offset Offset
* @param bus_width The required I/O width (1, 2, or 4 bytes) to be
* used when reading from @p r.
*
* @retval bhnd_nvram_io success.
* @retval NULL if allocation fails, or an invalid argument
* is supplied.
*/
struct bhnd_nvram_io *
bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset,
bus_size_t size, u_int bus_width)
{
struct bhnd_nvram_iores *iores;
rman_res_t r_start, r_size;
/* Verify the bus width */
switch (bus_width) {
case 1:
case 2:
case 4:
/* valid */
break;
default:
BHND_NV_LOG("invalid bus width %u\n", bus_width);
return (NULL);
}
/* offset/size must not exceed our internal size_t representation,
* or our bus_size_t usage (note that BUS_SPACE_MAXSIZE may be less
* than 2^(sizeof(bus_size_t) * 32). */
if (size > SIZE_MAX || offset > SIZE_MAX) {
BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n",
(uintmax_t)offset, (uintmax_t)offset);
return (NULL);
}
if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE)
{
BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n",
(uintmax_t)offset, (uintmax_t)offset);
return (NULL);
}
/* offset/size fall within the resource's mapped range */
r_size = rman_get_size(r->res);
r_start = rman_get_start(r->res);
if (r_size < offset || r_size < size || r_size - size < offset)
return (NULL);
/* offset/size must be bus_width aligned */
if ((r_start + offset) % bus_width != 0) {
BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width "
"%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width);
return (NULL);
}
if (size % bus_width != 0) {
BHND_NV_LOG("size %#jx not aligned to bus width %u\n",
(uintmax_t)size, bus_width);
return (NULL);
}
/* Allocate and return the I/O context */
iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK);
iores->io.iops = &bhnd_nvram_iores_ops;
iores->res = r;
iores->offset = offset;
iores->size = size;
iores->bus_width = bus_width;
return (&iores->io);
}
static void
bhnd_nvram_iores_free(struct bhnd_nvram_io *io)
{
free(io, M_BHND_NVRAM);
}
static size_t
bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io;
return (iores->size);
}
static int
bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset,
void **ptr, size_t nbytes, size_t *navail)
{
/* unsupported */
return (ENODEV);
}
/**
* Validate @p offset and @p nbytes:
*
* - Verify that @p offset is mapped by the backing resource.
* - If less than @p nbytes are available at @p offset, write the actual number
* of bytes available to @p nbytes.
* - Verify that @p offset + @p nbytes are correctly aligned.
*/
static int
bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset,
size_t *nbytes)
{
/* Verify offset falls within the resource range */
if (offset > iores->size)
return (ENXIO);
/* Check for eof */
if (offset == iores->size) {
*nbytes = 0;
return (0);
}
/* Verify offset alignment */
if (offset % iores->bus_width != 0)
return (EFAULT);
/* Limit nbytes to available range and verify size alignment */
*nbytes = ummin(*nbytes, iores->size - offset);
if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0)
return (EFAULT);
return (0);
}
static int
bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
struct bhnd_nvram_iores *iores;
bus_size_t r_offset;
size_t navail;
int error;
iores = (struct bhnd_nvram_iores *)io;
/* Validate the request and determine the actual number of readable
* bytes */
navail = nbytes;
if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
return (error);
/* At least nbytes must be readable */
if (navail < nbytes)
return (ENXIO);
/* Handle zero length read */
if (nbytes == 0)
return (0);
/* Determine actual resource offset and perform the read */
r_offset = iores->offset + offset;
switch (iores->bus_width) {
case 1:
bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer,
nbytes);
break;
case 2:
bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer,
nbytes / 2);
break;
case 4:
bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer,
nbytes / 4);
break;
default:
panic("unreachable!");
}
return (0);
}
static int
bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset,
void *buffer, size_t nbytes)
{
struct bhnd_nvram_iores *iores;
size_t navail;
bus_size_t r_offset;
int error;
iores = (struct bhnd_nvram_iores *)io;
/* Validate the request and determine the actual number of writable
* bytes */
navail = nbytes;
if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
return (error);
/* At least nbytes must be writable */
if (navail < nbytes)
return (ENXIO);
/* Determine actual resource offset and perform the write */
r_offset = iores->offset + offset;
switch (iores->bus_width) {
case 1:
bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer,
nbytes);
break;
case 2:
bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer,
nbytes / 2);
break;
case 4:
bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer,
nbytes / 4);
break;
default:
panic("unreachable!");
}
return (0);
}

View File

@ -0,0 +1,106 @@
/*-
* Copyright (c) 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_IOVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_IOVAR_H_
#include <sys/param.h>
#include "bhnd_nvram_io.h"
/** @see bhnd_nvram_io_read() */
typedef int (bhnd_nvram_iop_read)(struct bhnd_nvram_io *io, size_t offset,
void *buffer, size_t nbytes);
/** @see bhnd_nvram_io_read_ptr() */
typedef int (bhnd_nvram_iop_read_ptr)(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail);
/** @see bhnd_nvram_io_write() */
typedef int (bhnd_nvram_iop_write)(struct bhnd_nvram_io *io, size_t offset,
void *buffer, size_t nbytes);
/** @see bhnd_nvram_io_write_ptr() */
typedef int (bhnd_nvram_iop_write_ptr)(struct bhnd_nvram_io *io, size_t offset,
void **ptr, size_t nbytes, size_t *navail);
/** @see bhnd_nvram_io_getsize() */
typedef size_t (bhnd_nvram_iop_getsize)(struct bhnd_nvram_io *io);
/** @see bhnd_nvram_io_setsize() */
typedef int (bhnd_nvram_iop_setsize)(struct bhnd_nvram_io *io, size_t size);
/** @see bhnd_nvram_io_free() */
typedef void (bhnd_nvram_iop_free)(struct bhnd_nvram_io *io);
/**
* NVRAM abstract I/O operations.
*/
struct bhnd_nvram_iops {
bhnd_nvram_iop_read *read; /**< read() implementation */
bhnd_nvram_iop_read_ptr *read_ptr; /**< read_ptr() implementation */
bhnd_nvram_iop_getsize *getsize; /**< getsize() implementation */
bhnd_nvram_iop_setsize *setsize; /**< setsize() implementation */
bhnd_nvram_iop_write *write; /**< write() implementation */
bhnd_nvram_iop_write_ptr *write_ptr; /**< write_ptr() implementation */
bhnd_nvram_iop_free *free; /**< free() implementation */
};
/**
* NVRAM abstract I/O context.
*/
struct bhnd_nvram_io {
const struct bhnd_nvram_iops *iops;
};
/**
* Declare a bhnd_nvram_iops class with name @p _n.
*/
#define BHND_NVRAM_IOPS_DEFN(_n) \
static bhnd_nvram_iop_read bhnd_nvram_ ## _n ## _read; \
static bhnd_nvram_iop_read_ptr bhnd_nvram_ ## _n ## _read_ptr; \
static bhnd_nvram_iop_write bhnd_nvram_ ## _n ## _write; \
static bhnd_nvram_iop_write_ptr bhnd_nvram_ ## _n ## _write_ptr;\
static bhnd_nvram_iop_getsize bhnd_nvram_ ## _n ## _getsize; \
static bhnd_nvram_iop_setsize bhnd_nvram_ ## _n ## _setsize; \
static bhnd_nvram_iop_free bhnd_nvram_ ## _n ## _free; \
\
static struct bhnd_nvram_iops bhnd_nvram_ ## _n ## _ops = { \
.read = bhnd_nvram_ ## _n ## _read, \
.read_ptr = bhnd_nvram_ ## _n ## _read_ptr, \
.write = bhnd_nvram_ ## _n ## _write, \
.write_ptr = bhnd_nvram_ ## _n ## _write_ptr, \
.getsize = bhnd_nvram_ ## _n ## _getsize, \
.setsize = bhnd_nvram_ ## _n ## _setsize, \
.free = bhnd_nvram_ ## _n ## _free \
};
#endif /* _BHND_NVRAM_BHND_NVRAM_IOVAR_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,101 +0,0 @@
/*-
* 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_PARSER_H_
#define _BHND_NVRAM_BHND_NVRAM_PARSER_H_
#include <sys/param.h>
#include <sys/bus.h>
#include "bhnd_nvram_common.h"
union bhnd_nvram_ident;
struct bhnd_nvram_idx;
struct bhnd_nvram_ops;
struct bhnd_nvram_devpath;
struct bhnd_nvram;
LIST_HEAD(bhnd_nvram_devpaths, bhnd_nvram_devpath);
int bhnd_nvram_parser_identify(const union bhnd_nvram_ident *ident,
bhnd_nvram_format expected);
int bhnd_nvram_parser_init(struct bhnd_nvram *sc, device_t owner,
const void *data, size_t len, bhnd_nvram_format fmt);
void bhnd_nvram_parser_fini(struct bhnd_nvram *sc);
int bhnd_nvram_parser_getvar(struct bhnd_nvram *sc, const char *name,
void *buf, size_t *len, bhnd_nvram_type type);
int bhnd_nvram_parser_setvar(struct bhnd_nvram *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type);
/** BCM NVRAM header */
struct bhnd_nvram_header {
uint32_t magic;
uint32_t size;
uint32_t cfg0; /**< crc:8, version:8, sdram_init:16 */
uint32_t cfg1; /**< sdram_config:16, sdram_refresh:16 */
uint32_t sdram_ncdl; /**< sdram_ncdl */
} __packed;
/**
* NVRAM format identification.
*
* To perform identification of the NVRAM format using bhnd_nvram_identify(),
* read `sizeof(bhnd_nvram_indent)` bytes from the head of the NVRAM data.
*/
union bhnd_nvram_ident {
struct bhnd_nvram_header bcm;
char btxt[4];
struct bhnd_tlv_ident {
uint8_t tag;
uint8_t size[2];
uint8_t flags;
} __packed tlv;
};
/** bhnd nvram parser instance state */
struct bhnd_nvram {
device_t dev; /**< parent device, or NULL */
const struct bhnd_nvram_ops *ops;
uint8_t *buf; /**< nvram data */
size_t buf_size;
size_t num_buf_vars; /**< number of records in @p buf (0 if not yet calculated) */
struct bhnd_nvram_idx *idx; /**< key index */
struct bhnd_nvram_devpaths devpaths; /**< device paths */
struct bhnd_nvram_varmap defaults; /**< default values */
struct bhnd_nvram_varmap pending; /**< uncommitted writes */
};
#endif /* _BHND_NVRAM_BHND_NVRAM_PARSER_H_ */

View File

@ -1,86 +0,0 @@
/*-
* 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_PARSERVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_
#include <sys/types.h>
#include "bhnd_nvram_common.h"
#include "bhnd_nvram_parser.h"
#define NVRAM_IDX_VAR_THRESH 15 /**< index is generated if minimum variable count is met */
#define NVRAM_IDX_OFFSET_MAX UINT16_MAX /**< maximum indexable offset */
#define NVRAM_IDX_LEN_MAX UINT8_MAX /**< maximum indexable key/value length */
#define NVRAM_KEY_MAX 64 /**< maximum key length (not incl. NUL) */
#define NVRAM_VAL_MAX 255 /**< maximum value length (not incl. NUL) */
#define NVRAM_DEVPATH_STR "devpath" /**< name prefix of device path aliases */
#define NVRAM_DEVPATH_LEN (sizeof(NVRAM_DEVPATH_STR) - 1)
#define NVRAM_SMALL_HASH_SIZE 16 /**< hash table size for pending/default tuples */
/**
* NVRAM devpath record.
*
* Aliases index values to full device paths.
*/
struct bhnd_nvram_devpath {
u_long index; /** alias index */
char *path; /** aliased path */
LIST_ENTRY(bhnd_nvram_devpath) dp_link;
};
/**
* NVRAM index record.
*
* Provides entry offsets into a backing NVRAM buffer.
*/
struct bhnd_nvram_idx_entry {
uint16_t env_offset; /**< offset to env string */
uint8_t key_len; /**< key length */
uint8_t val_len; /**< value length */
};
/**
* NVRAM index.
*
* Provides a compact binary search index into the backing NVRAM buffer.
*/
struct bhnd_nvram_idx {
size_t num_entries; /**< entry count */
struct bhnd_nvram_idx_entry entries[]; /**< index entries */
};
#endif /* _BHND_NVRAM_BHND_NVRAM_PARSERVAR_H_ */

View File

@ -0,0 +1,402 @@
/*-
* 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_ */

View File

@ -0,0 +1,572 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/queue.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#else /* !_KERNEL */
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_storevar.h"
/*
* BHND NVRAM Store
*
* Manages in-memory and persistent representations of NVRAM data.
*/
static int bhnd_nvram_sort_idx(void *ctx, const void *lhs,
const void *rhs);
static int bhnd_nvram_generate_index(struct bhnd_nvram_store *sc);
static void *bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc,
const char *name);
/**
* Allocate and initialize a new NVRAM data store instance.
*
* The caller is responsible for deallocating the instance via
* bhnd_nvram_store_free().
*
* @param[out] store On success, a pointer to the newly allocated NVRAM data
* instance.
* @param data The NVRAM data to be managed by the returned NVRAM data store
* instance.
*
* @retval 0 success
* @retval non-zero if an error occurs during allocation or initialization, a
* regular unix error code will be returned.
*/
int
bhnd_nvram_store_new(struct bhnd_nvram_store **store,
struct bhnd_nvram_data *data)
{
struct bhnd_nvram_store *sc;
int error;
/* Allocate new instance */
sc = bhnd_nv_calloc(1, sizeof(*sc));
if (sc == NULL)
return (ENOMEM);
LIST_INIT(&sc->paths);
/* Retain the NVRAM data */
sc->nv = bhnd_nvram_data_retain(data);
/* Allocate uncommitted change list */
sc->pending = nvlist_create(NV_FLAG_IGNORE_CASE);
if (sc->pending == NULL) {
error = ENOMEM;
goto cleanup;
}
/* Generate all indices */
if ((error = bhnd_nvram_generate_index(sc)))
goto cleanup;
BHND_NVSTORE_LOCK_INIT(sc);
*store = sc;
return (0);
cleanup:
bhnd_nvram_store_free(sc);
return (error);
}
/**
* Allocate and initialize a new NVRAM data store instance, parsing the
* NVRAM data from @p io.
*
* The caller is responsible for deallocating the instance via
* bhnd_nvram_store_free().
*
* The NVRAM data mapped by @p io will be copied, and @p io may be safely
* deallocated after bhnd_nvram_store_new() returns.
*
* @param[out] store On success, a pointer to the newly allocated NVRAM data
* instance.
* @param io An I/O context mapping the NVRAM data to be copied and parsed.
* @param cls The NVRAM data class to be used when parsing @p io, or NULL
* to perform runtime identification of the appropriate data class.
*
* @retval 0 success
* @retval non-zero if an error occurs during allocation or initialization, a
* regular unix error code will be returned.
*/
int
bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *cls)
{
struct bhnd_nvram_data *data;
int error;
/* Try to parse the data */
if ((error = bhnd_nvram_data_new(cls, &data, io)))
return (error);
/* Try to create our new store instance */
error = bhnd_nvram_store_new(store, data);
bhnd_nvram_data_release(data);
return (error);
}
/**
* Free an NVRAM store instance, releasing all associated resources.
*
* @param sc A store instance previously allocated via
* bhnd_nvram_store_new().
*/
void
bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
{
struct bhnd_nvstore_path *dpath, *dnext;
LIST_FOREACH_SAFE(dpath, &sc->paths, dp_link, dnext) {
bhnd_nv_free(dpath->path);
bhnd_nv_free(dpath);
}
if (sc->pending != NULL)
nvlist_destroy(sc->pending);
if (sc->idx != NULL)
bhnd_nv_free(sc->idx);
if (sc->nv != NULL)
bhnd_nvram_data_release(sc->nv);
BHND_NVSTORE_LOCK_DESTROY(sc);
bhnd_nv_free(sc);
}
/**
* Read an NVRAM variable.
*
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param[out] buf On success, the requested value will be written
* to this buffer. This argment may be NULL if
* the value is not desired.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
* @param type The requested data type to be written to @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
* small to hold the requested value.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
int
bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
void *buf, size_t *len, bhnd_nvram_type type)
{
void *cookiep;
const void *inp;
size_t ilen;
bhnd_nvram_type itype;
int error;
/*
* Search order:
*
* - uncommitted changes
* - index lookup OR buffer scan
*/
BHND_NVSTORE_LOCK(sc);
/* Is variable marked for deletion? */
if (nvlist_exists_null(sc->pending, name)) {
BHND_NVSTORE_UNLOCK(sc);
return (ENOENT);
}
/* Does an uncommitted value exist? */
if (nvlist_exists_string(sc->pending, name)) {
/* Uncommited value exists, is not a deletion */
inp = nvlist_get_string(sc->pending, name);
ilen = strlen(inp) + 1;
itype = BHND_NVRAM_TYPE_STRING;
/* Coerce borrowed data reference before releasing
* our lock. */
error = bhnd_nvram_value_coerce(inp, ilen, itype, buf, len,
type);
BHND_NVSTORE_UNLOCK(sc);
return (error);
} else if (nvlist_exists(sc->pending, name)) {
BHND_NV_PANIC("invalid value type for pending change %s", name);
}
/* Fetch variable from parsed NVRAM data. */
if ((cookiep = bhnd_nvram_index_lookup(sc, name)) == NULL) {
BHND_NVSTORE_UNLOCK(sc);
return (ENOENT);
}
/* Let the parser itself perform value coercion */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, buf, len, type);
BHND_NVSTORE_UNLOCK(sc);
return (error);
}
/**
* Set an NVRAM variable.
*
* @param sc The NVRAM parser state.
* @param name The NVRAM variable name.
* @param[out] buf The new value.
* @param[in,out] len The size of @p buf.
* @param type The data type of @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval EINVAL If @p len does not match the expected variable size.
*/
int
bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type)
{
const char *inp;
char vbuf[512];
/* Verify name validity */
if (!bhnd_nvram_validate_name(name, strlen(name)))
return (EINVAL);
/* Verify buffer size alignment for the given type. If this is a
* variable width type, a width of 0 will always pass this check */
if (len % bhnd_nvram_value_size(type, buf, len, 1) != 0)
return (EINVAL);
/* Determine string format (or directly add variable, if a C string) */
switch (type) {
case BHND_NVRAM_TYPE_UINT8:
case BHND_NVRAM_TYPE_UINT16:
case BHND_NVRAM_TYPE_UINT32:
case BHND_NVRAM_TYPE_UINT64:
case BHND_NVRAM_TYPE_INT8:
case BHND_NVRAM_TYPE_INT16:
case BHND_NVRAM_TYPE_INT32:
case BHND_NVRAM_TYPE_INT64:
case BHND_NVRAM_TYPE_UINT8_ARRAY:
case BHND_NVRAM_TYPE_UINT16_ARRAY:
case BHND_NVRAM_TYPE_UINT32_ARRAY:
case BHND_NVRAM_TYPE_UINT64_ARRAY:
case BHND_NVRAM_TYPE_INT8_ARRAY:
case BHND_NVRAM_TYPE_INT16_ARRAY:
case BHND_NVRAM_TYPE_INT32_ARRAY:
case BHND_NVRAM_TYPE_INT64_ARRAY:
case BHND_NVRAM_TYPE_CHAR_ARRAY:
case BHND_NVRAM_TYPE_STRING_ARRAY:
// TODO: non-char/string value support
return (EOPNOTSUPP);
case BHND_NVRAM_TYPE_CHAR:
case BHND_NVRAM_TYPE_STRING:
inp = buf;
/* Must not exceed buffer size */
if (len > sizeof(vbuf))
return (EINVAL);
/* Must have room for a trailing NUL */
if (len == sizeof(vbuf) && inp[len-1] != '\0')
return (EINVAL);
/* Copy out the string value and append trailing NUL */
strlcpy(vbuf, buf, len);
/* Add to pending change list */
BHND_NVSTORE_LOCK(sc);
nvlist_add_string(sc->pending, name, vbuf);
BHND_NVSTORE_UNLOCK(sc);
}
return (0);
}
/* sort function for bhnd_nvstore_index cookie values */
static int
bhnd_nvram_sort_idx(void *ctx, const void *lhs, const void *rhs)
{
struct bhnd_nvram_store *sc;
const char *l_str, *r_str;
sc = ctx;
/* Fetch string pointers from the cookiep values */
l_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)lhs);
r_str = bhnd_nvram_data_getvar_name(sc->nv, *(void * const *)rhs);
/* Perform comparison */
return (strcasecmp(l_str, r_str));
}
/**
* Parse and register all device paths and path aliases in @p nvram.
*
* @param sc The NVRAM parser state.
*
* @retval 0 success
* @retval non-zero If registering device paths fails, a regular unix
* error code will be returned.
*/
static int
bhnd_nvram_register_devpaths(struct bhnd_nvram_store *sc)
{
const char *name;
void *cookiep;
int error;
/* Skip if backing parser does not support device paths */
if (!(bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_DEVPATHS))
return (0);
/* Parse and register all device path aliases */
cookiep = NULL;
while ((name = bhnd_nvram_data_next(sc->nv, &cookiep))) {
struct bhnd_nvstore_path *devpath;
const char *suffix;
char *eptr;
char *path;
size_t path_len;
u_long index;
path = NULL;
/* Check for devpath prefix */
if (strncmp(name, "devpath", strlen("devpath")) != 0)
continue;
/* Parse index value that should follow a 'devpath' prefix */
suffix = name + strlen("devpath");
index = strtoul(suffix, &eptr, 10);
if (eptr == suffix || *eptr != '\0') {
BHND_NV_LOG("invalid devpath variable '%s'\n", name);
continue;
}
/* Determine path value length */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, NULL, &path_len,
BHND_NVRAM_TYPE_STRING);
if (error)
return (error);
/* Allocate path buffer */
if ((path = bhnd_nv_malloc(path_len)) == NULL)
return (ENOMEM);
/* Decode to our new buffer */
error = bhnd_nvram_data_getvar(sc->nv, cookiep, path, &path_len,
BHND_NVRAM_TYPE_STRING);
if (error) {
bhnd_nv_free(path);
return (error);
}
/* Register path alias */
devpath = bhnd_nv_malloc(sizeof(*devpath));
if (devpath == NULL) {
bhnd_nv_free(path);
return (ENOMEM);
}
devpath->index = index;
devpath->path = path;
LIST_INSERT_HEAD(&sc->paths, devpath, dp_link);
}
return (0);
}
/**
* Generate all indices for the NVRAM data backing @p nvram.
*
* @param sc The NVRAM parser state.
*
* @retval 0 success
* @retval non-zero If indexing @p nvram fails, a regular unix
* error code will be returned.
*/
static int
bhnd_nvram_generate_index(struct bhnd_nvram_store *sc)
{
const char *name;
void *cookiep;
size_t idx_bytes;
size_t num_vars;
int error;
/* Parse and register all device path aliases */
if ((error = bhnd_nvram_register_devpaths(sc)))
return (error);
/* Skip generating a variable index if threshold is not met ... */
num_vars = bhnd_nvram_data_count(sc->nv);
if (num_vars < NVRAM_IDX_VAR_THRESH)
return (0);
/* ... or if the backing data instance implements indexed lookup
* internally */
if (bhnd_nvram_data_caps(sc->nv) & BHND_NVRAM_DATA_CAP_INDEXED)
return (0);
/* Allocate and populate variable index */
idx_bytes = sizeof(struct bhnd_nvstore_index) +
(sizeof(void *) * num_vars);
sc->idx = bhnd_nv_malloc(idx_bytes);
if (sc->idx == NULL) {
BHND_NV_LOG("error allocating %zu byte index\n", idx_bytes);
goto bad_index;
}
sc->idx->num_cookiep = num_vars;
#ifdef _KERNEL
if (bootverbose) {
BHND_NV_LOG("allocated %zu byte index for %zu variables\n",
idx_bytes, num_vars);
}
#endif /* _KERNEL */
cookiep = NULL;
for (size_t i = 0; i < sc->idx->num_cookiep; i++) {
/* Fetch next entry */
name = bhnd_nvram_data_next(sc->nv, &cookiep);
/* Early EOF */
if (name == NULL) {
BHND_NV_LOG("indexing failed, expected %zu records "
"(got %zu)\n", sc->idx->num_cookiep, i+1);
goto bad_index;
}
/* Save the variable's cookiep */
sc->idx->cookiep[i] = cookiep;
}
/* Sort the index table */
qsort_r(sc->idx->cookiep, sc->idx->num_cookiep,
sizeof(sc->idx->cookiep[0]), sc, bhnd_nvram_sort_idx);
return (0);
bad_index:
/* Fall back on non-indexed access */
BHND_NV_LOG("reverting to non-indexed variable lookup\n");
if (sc->idx != NULL) {
bhnd_nv_free(sc->idx);
sc->idx = NULL;
}
return (0);
}
/**
* Perform an index lookup of @p name, returning the associated cookie
* value, or NULL if the variable does not exist.
*
* @param sc The NVRAM parser state.
* @param name The variable to search for.
*/
static void *
bhnd_nvram_index_lookup(struct bhnd_nvram_store *sc, const char *name)
{
void *cookiep;
const char *indexed_name;
size_t min, mid, max;
int order;
BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
if (sc->idx == NULL || sc->idx->num_cookiep == 0)
return (bhnd_nvram_data_find(sc->nv, name));
/*
* Locate the requested variable using a binary search.
*/
BHND_NV_ASSERT(sc->idx->num_cookiep > 0,
("empty array causes underflow"));
min = 0;
max = sc->idx->num_cookiep - 1;
while (max >= min) {
/* Select midpoint */
mid = (min + max) / 2;
cookiep = sc->idx->cookiep[mid];
/* Determine which side of the partition to search */
indexed_name = bhnd_nvram_data_getvar_name(sc->nv, cookiep);
order = strcasecmp(indexed_name, name);
if (order < 0) {
/* Search upper partition */
min = mid + 1;
} else if (order > 0) {
/* Search (non-empty) lower partition */
if (mid == 0)
break;
max = mid - 1;
} else if (order == 0) {
/* Match found */
return (cookiep);
}
}
/* Not found */
return (NULL);
}

View File

@ -29,40 +29,40 @@
* $FreeBSD$ * $FreeBSD$
*/ */
#ifndef _BHND_NVRAM_BHND_NVRAMVAR_H_ #ifndef _BHND_NVRAM_BHND_NVRAM_STORE_H_
#define _BHND_NVRAM_BHND_NVRAMVAR_H_ #define _BHND_NVRAM_BHND_NVRAM_STORE_H_
#ifdef _KERNEL
#include <sys/param.h> #include <sys/param.h>
#include <sys/bus.h> #include <sys/bus.h>
#include <sys/nv.h>
#else /* !_KERNEL */
#include <errno.h>
#include "bhnd_nvram_parser.h" #include <nv.h>
DECLARE_CLASS(bhnd_nvram_driver); #include <stdint.h>
#include <stdlib.h>
#endif
int bhnd_nvram_probe(device_t dev); #include <sys/queue.h>
int bhnd_nvram_attach(device_t dev, void *data, size_t size,
bhnd_nvram_format fmt);
int bhnd_nvram_resume(device_t dev);
int bhnd_nvram_suspend(device_t dev);
int bhnd_nvram_detach(device_t dev);
/** #include "bhnd_nvram_data.h"
* bhnd_nvram driver instance state. Must be first member of all subclass #include "bhnd_nvram_io.h"
* softc structures.
*/
struct bhnd_nvram_softc {
device_t dev;
struct mtx mtx; /**< nvram mutex */
struct bhnd_nvram nvram; /**< nvram shadow */
};
struct bhnd_nvram_store;
#define BHND_NVRAM_LOCK_INIT(sc) \ int bhnd_nvram_store_new(struct bhnd_nvram_store **store,
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ struct bhnd_nvram_data *data);
"bhnd_nvram lock", MTX_DEF)
#define BHND_NVRAM_LOCK(sc) mtx_lock(&(sc)->mtx)
#define BHND_NVRAM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define BHND_NVRAM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define BHND_NVRAM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
#endif /* _BHND_NVRAM_BHND_NVRAMVAR_H_ */ int bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
struct bhnd_nvram_io *io, bhnd_nvram_data_class_t *cls);
void bhnd_nvram_store_free(struct bhnd_nvram_store *store);
int bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
void *buf, size_t *len, bhnd_nvram_type type);
int bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type);
#endif /* _BHND_NVRAM_BHND_NVRAM_STORE_H_ */

View File

@ -0,0 +1,111 @@
/*-
* 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_STOREVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_STOREVAR_H_
#include <sys/types.h>
#ifndef _KERNEL
#include <pthread.h>
#endif
#include "bhnd_nvram_store.h"
/** Index is only generated if minimum variable count is met */
#define NVRAM_IDX_VAR_THRESH 15
#define BHND_NVSTORE_PATH_ALIAS_NONE ULONG_MAX
LIST_HEAD(bhnd_nvstore_paths, bhnd_nvstore_path);
/**
* NVRAM store path.
*/
struct bhnd_nvstore_path {
char *path; /** relative path */
u_long index; /** aliased path index, or
BHND_NVSTORE_PATH_IDX_INVALID */
LIST_ENTRY(bhnd_nvstore_path) dp_link;
};
/**
* NVRAM store index.
*
* Provides effecient name-based lookup by maintaining an array of cached
* cookiep values, sorted lexicographically by variable name.
*/
struct bhnd_nvstore_index {
size_t num_cookiep; /**< cookiep count */
void *cookiep[]; /**< cookiep values */
};
/** bhnd nvram store instance state */
struct bhnd_nvram_store {
#ifdef _KERNEL
struct mtx mtx;
#else
pthread_mutex_t mtx;
#endif
struct bhnd_nvram_data *nv; /**< backing data */
struct bhnd_nvstore_index *idx; /**< index, or NULL */
struct bhnd_nvstore_paths paths; /**< paths */
nvlist_t *pending; /**< uncommitted writes */
};
#ifdef _KERNEL
#define BHND_NVSTORE_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, "BHND NVRAM store lock", NULL, MTX_DEF)
#define BHND_NVSTORE_LOCK(sc) mtx_lock(&(sc)->mtx)
#define BHND_NVSTORE_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define BHND_NVSTORE_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define BHND_NVSTORE_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
#else /* !_KERNEL */
#define BHND_NVSTORE_LOCK_INIT(sc) do { \
int error = pthread_mutex_init(&(sc)->mtx, NULL); \
if (error) \
BHND_NV_PANIC("pthread_mutex_init() failed: %d", \
error); \
} while(0)
#define BHND_NVSTORE_LOCK(sc) pthread_mutex_lock(&(sc)->mtx)
#define BHND_NVSTORE_UNLOCK(sc) pthread_mutex_unlock(&(sc)->mtx)
#define BHND_NVSTORE_LOCK_DESTROY(sc) pthread_mutex_destroy(&(sc)->mtx)
#define BHND_NVSTORE_LOCK_ASSERT(sc, what)
#endif /* _KERNEL */
#endif /* _BHND_NVRAM_BHND_NVRAM_STOREVAR_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
/*-
* 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_VALUE_H_
#define _BHND_NVRAM_BHND_NVRAM_VALUE_H_
#include <sys/refcount.h>
#ifdef _KERNEL
#include <machine/stdarg.h>
#else /* !_KERNEL */
#include <stdarg.h>
#endif /* _KERNEL */
#include "bhnd_nvram.h"
typedef struct bhnd_nvram_val_fmt bhnd_nvram_val_fmt_t;
typedef struct bhnd_nvram_val bhnd_nvram_val_t;
int bhnd_nvram_val_init(bhnd_nvram_val_t *value,
const bhnd_nvram_val_fmt_t *fmt,
const void *inp, size_t ilen,
bhnd_nvram_type itype, uint32_t flags);
int bhnd_nvram_val_new(bhnd_nvram_val_t **value,
const bhnd_nvram_val_fmt_t *fmt,
const void *inp, size_t ilen,
bhnd_nvram_type itype, uint32_t flags);
bhnd_nvram_val_t *bhnd_nvram_val_copy(bhnd_nvram_val_t *value);
void bhnd_nvram_val_release(
bhnd_nvram_val_t *value);
int bhnd_nvram_val_encode(bhnd_nvram_val_t *value,
void *outp, size_t *olen,
bhnd_nvram_type otype);
int bhnd_nvram_val_encode_elem(
bhnd_nvram_val_t *value, const void *inp,
size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
int bhnd_nvram_val_printf(bhnd_nvram_val_t *value,
const char *fmt, char *outp, size_t *olen,
...);
int bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value,
const char *fmt, char *outp, size_t *olen,
va_list ap);
const void *bhnd_nvram_val_bytes(bhnd_nvram_val_t *value,
size_t *len, bhnd_nvram_type *itype);
bhnd_nvram_type bhnd_nvram_val_elem_type(
bhnd_nvram_val_t *value);
const void *bhnd_nvram_val_next(bhnd_nvram_val_t *value,
const void *prev, size_t *len);
size_t bhnd_nvram_val_nelem(bhnd_nvram_val_t *value);
/**
* NVRAM value flags
*/
enum {
/**
* Do not allocate additional space for value data; all data must be
* represented inline within the value structure (default).
*/
BHND_NVRAM_VAL_FIXED = (0<<0),
/**
* Automatically allocate additional space for value data if it cannot
* be represented within the value structure.
*/
BHND_NVRAM_VAL_DYNAMIC = (1<<0),
/**
* Copy the value data upon initialization. (default).
*/
BHND_NVRAM_VAL_COPY_DATA = (0<<1),
/**
* Do not perform an initial copy of the value data; the data must
* remain valid for the lifetime of the NVRAM value.
*
* Value data will still be copied if the value itself is copied to the
* heap.
*/
BHND_NVRAM_VAL_BORROW_DATA = (1<<1),
/**
* Do not copy the value data when copying the value to the heap; the
* vlaue data is assumed to be statically allocated and must remain
* valid for the lifetime of the process.
*
* Implies BHND_NVRAM_VAL_BORROW_DATA.
*/
BHND_NVRAM_VAL_STATIC_DATA = (1<<2),
};
/**
* @internal
*
* NVRAM value storage types.
*/
typedef enum {
/**
* The value structure has an automatic or static storage duration
* (e.g. it is stack allocated, or is otherwise externally managed),
* and no destructors will be run prior to deallocation of the value.
*
* When performing copy/retain, the existing structure must be copied
* to a new heap allocation.
*/
BHND_NVRAM_VAL_STORAGE_AUTO = 0,
/**
* The value structure was heap allocated and is fully managed by the
* the NVRAM value API.
*
* When performing copy/retain, the existing structure may be retained
* as-is.
*/
BHND_NVRAM_VAL_STORAGE_DYNAMIC = 2,
} bhnd_nvram_val_storage_t;
/**
* @internal
*
* NVRAM data storage types.
*/
typedef enum {
/** Value has no active representation. This is the default for
* zero-initialized value structures. */
BHND_NVRAM_VAL_DATA_NONE = 0,
/** Value data is represented inline */
BHND_NVRAM_VAL_DATA_INLINE = 1,
/**
* Value represented by an external reference to data with a static
* storage location. The data need not be copied if copying the value.
*/
BHND_NVRAM_VAL_DATA_EXT_STATIC = 2,
/**
* Value represented by weak external reference, which must be copied
* if copying the value */
BHND_NVRAM_VAL_DATA_EXT_WEAK = 3,
/**
* Value represented by an external reference that must be deallocated
* when deallocating the value
*/
BHND_NVRAM_VAL_DATA_EXT_ALLOC = 4,
} bhnd_nvram_val_data_storage_t;
/**
* NVRAM value
*/
struct bhnd_nvram_val {
volatile u_int refs; /**< reference count */
bhnd_nvram_val_storage_t val_storage; /**< value structure storage */
const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL for default behavior */
bhnd_nvram_val_data_storage_t data_storage; /**< data storage */
bhnd_nvram_type data_type; /**< data type */
size_t data_len; /**< data size */
/** data representation */
union {
uint8_t u8[8]; /**< 8-bit unsigned data */
uint16_t u16[4]; /**< 16-bit unsigned data */
uint32_t u32[2]; /**< 32-bit unsigned data */
uint32_t u64[1]; /**< 64-bit unsigned data */
int8_t i8[8]; /**< 8-bit signed data */
int16_t i16[4]; /**< 16-bit signed data */
int32_t i32[2]; /**< 32-bit signed data */
int64_t i64[1]; /**< 64-bit signed data */
unsigned char ch[8]; /**< 8-bit character data */
const void *ptr; /**< external data */
} data;
};
/** Declare a bhnd_nvram_val_fmt with name @p _n */
#define BHND_NVRAM_VAL_TYPE_DECL(_n) \
extern const bhnd_nvram_val_fmt_t bhnd_nvram_val_ ## _n ## _fmt;
BHND_NVRAM_VAL_TYPE_DECL(bcm_decimal);
BHND_NVRAM_VAL_TYPE_DECL(bcm_hex);
BHND_NVRAM_VAL_TYPE_DECL(bcm_leddc);
BHND_NVRAM_VAL_TYPE_DECL(bcm_macaddr);
BHND_NVRAM_VAL_TYPE_DECL(bcm_string);
#endif /* _BHND_NVRAM_BHND_NVRAM_VALUE_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,883 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/sbuf.h>
#ifdef _KERNEL
#include <sys/ctype.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/_inttypes.h>
#else /* !_KERNEL */
#include <ctype.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#endif /* _KERNEL */
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_valuevar.h"
#ifdef _KERNEL
#define bhnd_nv_hex2ascii(hex) hex2ascii(hex)
#else /* !_KERNEL */
static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex])
#endif /* _KERNEL */
/**
* Maximum size, in bytes, of a string-encoded NVRAM integer value, not
* including any prefix (0x, 0, etc).
*
* We assume the largest possible encoding is the base-2 representation
* of a 64-bit integer.
*/
#define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1)
/**
* Format a string representation of @p value using @p fmt, with, writing the
* result to @p outp.
*
* @param value The value to be formatted.
* @param fmt The format string.
* @param[out] outp On success, the string will be written to this
* buffer. This argment may be NULL if the value is
* not desired.
* @param[in,out] olen The capacity of @p outp. On success, will be set
* to the actual number of bytes required for the
* requested string encoding (including a trailing
* NUL).
*
* Refer to bhnd_nvram_val_vprintf() for full format string documentation.
*
* @retval 0 success
* @retval EINVAL If @p fmt contains unrecognized format string
* specifiers.
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
* is too small to hold the encoded value.
* @retval EFTYPE If value coercion from @p value to a single string
* value via @p fmt is unsupported.
* @retval ERANGE If value coercion of @p value would overflow (or
* underflow) the representation defined by @p fmt.
*/
int
bhnd_nvram_val_printf(bhnd_nvram_val_t *value, const char *fmt, char *outp,
size_t *olen, ...)
{
va_list ap;
int error;
va_start(ap, olen);
error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap);
va_end(ap);
return (error);
}
/**
* Format a string representation of the elements of @p value using @p fmt,
* writing the result to @p outp.
*
* @param value The value to be formatted.
* @param fmt The format string.
* @param[out] outp On success, the string will be written to this
* buffer. This argment may be NULL if the value is
* not desired.
* @param[in,out] olen The capacity of @p outp. On success, will be set
* to the actual number of bytes required for the
* requested string encoding (including a trailing
* NUL).
* @param ap Argument list.
*
* @par Format Strings
*
* Value format strings are similar, but not identical to, those used
* by printf(3).
*
* Format specifier format:
* %[repeat][flags][width][.precision][length modifier][specifier]
*
* The format specifier is interpreted as an encoding directive for an
* individual value element; each format specifier will fetch the next element
* from the value, encode the element as the appropriate type based on the
* length modifiers and specifier, and then format the result as a string.
*
* For example, given a string value of '0x000F', and a format specifier of
* '%#hhx', the value will be asked to encode its first element as
* BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit
* unsigned integer representation, producing a string value of "0xF".
*
* Repeat:
* - [digits] Repeatedly apply the format specifier to the input
* value's elements up to `digits` times. The delimiter
* must be passed as a string in the next variadic
* argument.
* - [] Repeatedly apply the format specifier to the input
* value's elements until all elements have been. The
* processed. The delimiter must be passed as a string in
* the next variadic argument.
* - [*] Repeatedly apply the format specifier to the input
* value's elements. The repeat count is read from the
* next variadic argument as a size_t value
*
* Flags:
* - '#' use alternative form (e.g. 0x/0X prefixing of hex
* strings).
* - '0' zero padding
* - '-' left adjust padding
* - '+' include a sign character
* - ' ' include a space in place of a sign character for
* positive numbers.
*
* Width/Precision:
* - digits minimum field width.
* - * read the minimum field width from the next variadic
* argument as a ssize_t value. A negative value enables
* left adjustment.
* - .digits field precision.
* - .* read the field precision from the next variadic argument
* as a ssize_t value. A negative value enables left
* adjustment.
*
* Length Modifiers:
* - 'hh', 'I8' Convert the value to an 8-bit signed or unsigned
* integer.
* - 'h', 'I16' Convert the value to an 16-bit signed or unsigned
* integer.
* - 'l', 'I32' Convert the value to an 32-bit signed or unsigned
* integer.
* - 'll', 'j', 'I64' Convert the value to an 64-bit signed or unsigned
* integer.
*
* Data Specifiers:
* - 'd', 'i' Convert and format as a signed decimal integer.
* - 'u' Convert and format as an unsigned decimal integer.
* - 'o' Convert and format as an unsigned octal integer.
* - 'x' Convert and format as an unsigned hexadecimal integer,
* using lowercase hex digits.
* - 'X' Convert and format as an unsigned hexadecimal integer,
* using uppercase hex digits.
* - 's' Convert and format as a string.
* - '%' Print a literal '%' character.
*
* @retval 0 success
* @retval EINVAL If @p fmt contains unrecognized format string
* specifiers.
* @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
* is too small to hold the encoded value.
* @retval EFTYPE If value coercion from @p value to a single string
* value via @p fmt is unsupported.
* @retval ERANGE If value coercion of @p value would overflow (or
* underflow) the representation defined by @p fmt.
*/
int
bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value, const char *fmt, char *outp,
size_t *olen, va_list ap)
{
const void *elem;
size_t elen;
size_t limit, nbytes;
int error;
elem = NULL;
/* Determine output byte limit */
nbytes = 0;
if (outp != NULL)
limit = *olen;
else
limit = 0;
#define WRITE_CHAR(_c) do { \
if (limit > nbytes) \
*(outp + nbytes) = _c; \
\
if (nbytes == SIZE_MAX) \
return (EFTYPE); \
nbytes++; \
} while (0)
/* Encode string value as per the format string */
for (const char *p = fmt; *p != '\0'; p++) {
const char *delim;
size_t precision, width, delim_len;
u_long repeat, bits;
bool alt_form, ladjust, have_precision;
char padc, signc, lenc;
padc = ' ';
signc = '\0';
lenc = '\0';
delim = "";
delim_len = 0;
ladjust = false;
alt_form = false;
have_precision = false;
precision = 1;
bits = 32;
width = 0;
repeat = 1;
/* Copy all input to output until we hit a format specifier */
if (*p != '%') {
WRITE_CHAR(*p);
continue;
}
/* Hit '%' -- is this followed by an escaped '%' literal? */
p++;
if (*p == '%') {
WRITE_CHAR('%');
p++;
continue;
}
/* Parse repeat specifier */
if (*p == '[') {
p++;
/* Determine repeat count */
if (*p == ']') {
/* Repeat consumes all input */
repeat = bhnd_nvram_val_nelem(value);
} else if (*p == '*') {
/* Repeat is supplied as an argument */
repeat = va_arg(ap, size_t);
p++;
} else {
char *endp;
/* Repeat specified as argument */
repeat = strtoul(p, &endp, 10);
if (p == endp) {
BHND_NV_LOG("error parsing repeat "
"count at '%s'", p);
return (EINVAL);
}
/* Advance past repeat count */
p = endp;
}
/* Advance past terminating ']' */
if (*p != ']') {
BHND_NV_LOG("error parsing repeat count at "
"'%s'", p);
return (EINVAL);
}
p++;
delim = va_arg(ap, const char *);
delim_len = strlen(delim);
}
/* Parse flags */
while (*p != '\0') {
const char *np;
bool stop;
stop = false;
np = p+1;
switch (*p) {
case '#':
alt_form = true;
break;
case '0':
padc = '0';
break;
case '-':
ladjust = true;
break;
case ' ':
/* Must not override '+' */
if (signc != '+')
signc = ' ';
break;
case '+':
signc = '+';
break;
default:
/* Non-flag character */
stop = true;
break;
}
if (stop)
break;
else
p = np;
}
/* Parse minimum width */
if (*p == '*') {
ssize_t arg;
/* Width is supplied as an argument */
arg = va_arg(ap, int);
/* Negative width argument is interpreted as
* '-' flag followed by positive width */
if (arg < 0) {
ladjust = true;
arg = -arg;
}
width = arg;
p++;
} else if (bhnd_nv_isdigit(*p)) {
uint32_t v;
size_t len, parsed;
/* Parse width value */
len = sizeof(v);
error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed,
&v, &len, BHND_NVRAM_TYPE_UINT32);
if (error) {
BHND_NV_LOG("error parsing width %s: %d\n", p,
error);
return (EINVAL);
}
/* Save width and advance input */
width = v;
p += parsed;
}
/* Parse precision */
if (*p == '.') {
uint32_t v;
size_t len, parsed;
p++;
have_precision = true;
if (*p == '*') {
ssize_t arg;
/* Precision is specified as an argument */
arg = va_arg(ap, int);
/* Negative precision argument is interpreted
* as '-' flag followed by positive
* precision */
if (arg < 0) {
ladjust = true;
arg = -arg;
}
precision = arg;
} else if (!bhnd_nv_isdigit(*p)) {
/* Implicit precision of 0 */
precision = 0;
} else {
/* Parse precision value */
len = sizeof(v);
error = bhnd_nvram_parse_int(p, strlen(p), 10,
&parsed, &v, &len,
BHND_NVRAM_TYPE_UINT32);
if (error) {
BHND_NV_LOG("error parsing width %s: "
"%d\n", p, error);
return (EINVAL);
}
/* Save precision and advance input */
precision = v;
p += parsed;
}
}
/* Parse length modifiers */
while (*p != '\0') {
const char *np;
bool stop;
stop = false;
np = p+1;
switch (*p) {
case 'h':
if (lenc == '\0') {
/* Set initial length value */
lenc = *p;
bits = 16;
} else if (lenc == *p && bits == 16) {
/* Modify previous length value */
bits = 8;
} else {
BHND_NV_LOG("invalid length modifier "
"%c\n", *p);
return (EINVAL);
}
break;
case 'l':
if (lenc == '\0') {
/* Set initial length value */
lenc = *p;
bits = 32;
} else if (lenc == *p && bits == 32) {
/* Modify previous length value */
bits = 64;
} else {
BHND_NV_LOG("invalid length modifier "
"%c\n", *p);
return (EINVAL);
}
break;
case 'j':
/* Conflicts with all other length
* specifications, and may only occur once */
if (lenc != '\0') {
BHND_NV_LOG("invalid length modifier "
"%c\n", *p);
return (EINVAL);
}
lenc = *p;
bits = 64;
break;
case 'I': {
char *endp;
/* Conflicts with all other length
* specifications, and may only occur once */
if (lenc != '\0') {
BHND_NV_LOG("invalid length modifier "
"%c\n", *p);
return (EINVAL);
}
lenc = *p;
/* Parse the length specifier value */
p++;
bits = strtoul(p, &endp, 10);
if (p == endp) {
BHND_NV_LOG("invalid size specifier: "
"%s\n", p);
return (EINVAL);
}
/* Advance input past the parsed integer */
np = endp;
break;
}
default:
/* Non-length modifier character */
stop = true;
break;
}
if (stop)
break;
else
p = np;
}
/* Parse conversion specifier and format the value(s) */
for (u_long n = 0; n < repeat; n++) {
bhnd_nvram_type arg_type;
size_t arg_size;
size_t i;
u_long base;
bool is_signed, is_upper;
is_signed = false;
is_upper = false;
base = 0;
/* Fetch next element */
elem = bhnd_nvram_val_next(value, elem, &elen);
if (elem == NULL) {
BHND_NV_LOG("format string references more "
"than %zu available value elements\n",
bhnd_nvram_val_nelem(value));
return (EINVAL);
}
/*
* If this is not the first value, append the delimiter.
*/
if (n > 0) {
size_t nremain = 0;
if (limit > nbytes)
nremain = limit - nbytes;
if (nremain >= delim_len)
memcpy(outp + nbytes, delim, delim_len);
/* Add delimiter length to the total byte count */
if (SIZE_MAX - nbytes < delim_len)
return (EFTYPE); /* overflows size_t */
nbytes += delim_len;
}
/* Parse integer conversion specifiers */
switch (*p) {
case 'd':
case 'i':
base = 10;
is_signed = true;
break;
case 'u':
base = 10;
break;
case 'o':
base = 8;
break;
case 'x':
base = 16;
break;
case 'X':
base = 16;
is_upper = true;
break;
}
/* Format argument */
switch (*p) {
#define NV_ENCODE_INT(_width) do { \
arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width : \
BHND_NVRAM_TYPE_UINT ## _width; \
arg_size = sizeof(v.u ## _width); \
error = bhnd_nvram_val_encode_elem(value, elem, elen, \
&v.u ## _width, &arg_size, arg_type); \
if (error) { \
BHND_NV_LOG("error encoding argument as %s: %d\n", \
bhnd_nvram_type_name(arg_type), error); \
return (error); \
} \
\
if (is_signed) { \
if (v.i ## _width < 0) { \
add_neg = true; \
numval = (int64_t)-(v.i ## _width); \
} else { \
numval = (int64_t) (v.i ## _width); \
} \
} else { \
numval = v.u ## _width; \
} \
} while(0)
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X': {
char numbuf[NV_NUMSTR_MAX];
char *sptr;
uint64_t numval;
size_t slen;
bool add_neg;
union {
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
int8_t i8;
int16_t i16;
int32_t i32;
int64_t i64;
} v;
add_neg = false;
/* If precision is specified, it overrides
* (and behaves identically) to a zero-prefixed
* minimum width */
if (have_precision) {
padc = '0';
width = precision;
ladjust = false;
}
/* If zero-padding is used, value must be right
* adjusted */
if (padc == '0')
ladjust = false;
/* Request encode to the appropriate integer
* type, and then promote to common 64-bit
* representation */
switch (bits) {
case 8:
NV_ENCODE_INT(8);
break;
case 16:
NV_ENCODE_INT(16);
break;
case 32:
NV_ENCODE_INT(32);
break;
case 64:
NV_ENCODE_INT(64);
break;
default:
BHND_NV_LOG("invalid length specifier: "
"%lu\n", bits);
return (EINVAL);
}
#undef NV_ENCODE_INT
/* If a precision of 0 is specified and the
* value is also zero, no characters should
* be produced */
if (have_precision && precision == 0 &&
numval == 0)
{
break;
}
/* Emit string representation to local buffer */
BHND_NV_ASSERT(base <= 16, ("invalid base"));
sptr = numbuf + nitems(numbuf) - 1;
for (slen = 0; slen < sizeof(numbuf); slen++) {
char c;
uint64_t n;
n = numval % base;
c = bhnd_nv_hex2ascii(n);
if (is_upper)
c = bhnd_nv_toupper(c);
sptr--;
*sptr = c;
numval /= (uint64_t)base;
if (numval == 0) {
slen++;
break;
}
}
arg_size = slen;
/* Reserve space for 0/0x prefix? */
if (alt_form) {
if (numval == 0) {
/* If 0, no prefix */
alt_form = false;
} else if (base == 8) {
arg_size += 1; /* 0 */
} else if (base == 16) {
arg_size += 2; /* 0x/0X */
}
}
/* Reserve space for ' ', '+', or '-' prefix? */
if (add_neg || signc != '\0') {
if (add_neg)
signc = '-';
arg_size++;
}
/* Right adjust (if using spaces) */
if (!ladjust && padc != '0') {
for (i = arg_size; i < width; i++)
WRITE_CHAR(padc);
}
if (signc != '\0')
WRITE_CHAR(signc);
if (alt_form) {
if (base == 8) {
WRITE_CHAR('0');
} else if (base == 16) {
WRITE_CHAR('0');
if (is_upper)
WRITE_CHAR('X');
else
WRITE_CHAR('x');
}
}
/* Right adjust (if using zeros) */
if (!ladjust && padc == '0') {
for (i = slen; i < width; i++)
WRITE_CHAR(padc);
}
/* Write the string to our output buffer */
if (limit > nbytes && limit - nbytes >= slen)
memcpy(outp + nbytes, sptr, slen);
/* Update the total byte count */
if (SIZE_MAX - nbytes < arg_size)
return (EFTYPE); /* overflows size_t */
nbytes += arg_size;
/* Left adjust */
for (i = arg_size; ladjust && i < width; i++)
WRITE_CHAR(padc);
break;
}
case 's': {
char *s;
size_t slen;
/* Query the total length of the element when
* converted to a string */
arg_type = BHND_NVRAM_TYPE_STRING;
error = bhnd_nvram_val_encode_elem(value, elem,
elen, NULL, &arg_size, arg_type);
if (error) {
BHND_NV_LOG("error encoding argument "
"as %s: %d\n",
bhnd_nvram_type_name(arg_type),
error);
return (error);
}
/* Do not include trailing NUL in the string
* length */
if (arg_size > 0)
arg_size--;
/* Right adjust */
for (i = arg_size; !ladjust && i < width; i++)
WRITE_CHAR(padc);
/* Determine output positition and remaining
* buffer space */
if (limit > nbytes) {
s = outp + nbytes;
slen = limit - nbytes;
} else {
s = NULL;
slen = 0;
}
/* Encode the string to our output buffer */
error = bhnd_nvram_val_encode_elem(value, elem,
elen, s, &slen, arg_type);
if (error && error != ENOMEM) {
BHND_NV_LOG("error encoding argument "
"as %s: %d\n",
bhnd_nvram_type_name(arg_type),
error);
return (error);
}
/* Update the total byte count */
if (SIZE_MAX - nbytes < arg_size)
return (EFTYPE); /* overflows size_t */
nbytes += arg_size;
/* Left adjust */
for (i = arg_size; ladjust && i < width; i++)
WRITE_CHAR(padc);
break;
}
case 'c': {
char c;
arg_type = BHND_NVRAM_TYPE_CHAR;
arg_size = bhnd_nvram_value_size(arg_type, NULL,
0, 1);
/* Encode as single character */
error = bhnd_nvram_val_encode_elem(value, elem,
elen, &c, &arg_size, arg_type);
if (error) {
BHND_NV_LOG("error encoding argument "
"as %s: %d\n",
bhnd_nvram_type_name(arg_type),
error);
return (error);
}
BHND_NV_ASSERT(arg_size == sizeof(c),
("invalid encoded size"));
/* Right adjust */
for (i = arg_size; !ladjust && i < width; i++)
WRITE_CHAR(padc);
WRITE_CHAR(padc);
/* Left adjust */
for (i = arg_size; ladjust && i < width; i++)
WRITE_CHAR(padc);
break;
}
}
}
}
/* Append terminating NUL */
if (limit > nbytes)
*(outp + nbytes) = '\0';
if (nbytes < SIZE_MAX)
nbytes++;
else
return (EFTYPE);
/* Report required space */
*olen = nbytes;
if (limit < nbytes) {
if (outp != NULL)
return (ENOMEM);
}
return (0);
}

View File

@ -0,0 +1,100 @@
/*-
* 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_VALUEVAR_H_
#define _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_
#include "bhnd_nvram_value.h"
int bhnd_nvram_val_generic_encode(bhnd_nvram_val_t *value,
void *outp, size_t *olen, bhnd_nvram_type otype);
int bhnd_nvram_val_generic_encode_elem(bhnd_nvram_val_t *value,
const void *inp, size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
const void *bhnd_nvram_val_generic_next(bhnd_nvram_val_t *value,
const void *prev, size_t *len);
/**
* Filter input data prior to initialization.
*
* This may be used to permit direct initialization from data types other than
* the default native_type defined by @p fmt.
*
* @param[in,out] fmt Indirect pointer to the NVRAM value format. If
* modified by the caller, initialization will be
* restarted and performed using the provided
* format instance.
* @param inp Input data.
* @param ilen Input data length.
* @param itype Input data type.
*
* @retval 0 If initialization from @p inp is supported.
* @retval EFTYPE If initialization from @p inp is unsupported.
* @retval EFAULT if @p ilen is not correctly aligned for elements of
* @p itype.
*/
typedef int (bhnd_nvram_val_op_filter)(const bhnd_nvram_val_fmt_t **fmt,
const void *inp, size_t ilen, bhnd_nvram_type itype);
/** @see bhnd_nvram_val_encode() */
typedef int (bhnd_nvram_val_op_encode)(bhnd_nvram_val_t *value, void *outp,
size_t *olen, bhnd_nvram_type otype);
/** @see bhnd_nvram_val_encode_elem() */
typedef int (bhnd_nvram_val_op_encode_elem)(bhnd_nvram_val_t *value,
const void *inp, size_t ilen, void *outp, size_t *olen,
bhnd_nvram_type otype);
/** @see bhnd_nvram_val_next() */
typedef const void *(bhnd_nvram_val_op_next)(bhnd_nvram_val_t *value,
const void *prev, size_t *len);
/** @see bhnd_nvram_val_nelem() */
typedef size_t (bhnd_nvram_val_op_nelem)(bhnd_nvram_val_t *value);
/**
* NVRAM value format.
*
* Provides a set of callbacks to support defining custom parsing
* and encoding/conversion behavior when representing values as
* instances of bhnd_nvram_val.
*/
struct bhnd_nvram_val_fmt {
const char *name; /**< type name */
bhnd_nvram_type native_type; /**< native value representation */
bhnd_nvram_val_op_filter *op_filter;
bhnd_nvram_val_op_encode *op_encode;
bhnd_nvram_val_op_encode_elem *op_encode_elem;
bhnd_nvram_val_op_nelem *op_nelem;
bhnd_nvram_val_op_next *op_next;
};
#endif /* _BHND_NVRAM_BHND_NVRAM_VALUEVAR_H_ */

View File

@ -52,15 +52,9 @@ __FBSDID("$FreeBSD$");
#include "bhnd_nvram_if.h" #include "bhnd_nvram_if.h"
#include "bhnd_spromvar.h" #include "bhnd_nvram_io.h"
#define SPROM_LOCK_INIT(sc) \ #include "bhnd_spromvar.h"
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"BHND SPROM lock", MTX_DEF)
#define SPROM_LOCK(sc) mtx_lock(&(sc)->mtx)
#define SPROM_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define SPROM_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
#define SPROM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
/** /**
* Default bhnd sprom driver implementation of DEVICE_PROBE(). * Default bhnd sprom driver implementation of DEVICE_PROBE().
@ -68,9 +62,6 @@ __FBSDID("$FreeBSD$");
int int
bhnd_sprom_probe(device_t dev) bhnd_sprom_probe(device_t dev)
{ {
/* Quiet by default */
if (!bootverbose)
device_quiet(dev);
device_set_desc(dev, "SPROM/OTP"); device_set_desc(dev, "SPROM/OTP");
/* Refuse wildcard attachments */ /* Refuse wildcard attachments */
@ -100,32 +91,62 @@ int
bhnd_sprom_attach(device_t dev, bus_size_t offset) bhnd_sprom_attach(device_t dev, bus_size_t offset)
{ {
struct bhnd_sprom_softc *sc; struct bhnd_sprom_softc *sc;
int error; struct bhnd_nvram_io *io;
struct bhnd_resource *r;
bus_size_t r_size, sprom_size;
int rid;
int error;
sc = device_get_softc(dev); sc = device_get_softc(dev);
sc->dev = dev; sc->dev = dev;
io = NULL;
/* Allocate SPROM resource */ /* Allocate SPROM resource */
sc->sprom_rid = 0; rid = 0;
sc->sprom_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, r = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
&sc->sprom_rid, RF_ACTIVE); if (r == NULL) {
if (sc->sprom_res == NULL) {
device_printf(dev, "failed to allocate resources\n"); device_printf(dev, "failed to allocate resources\n");
return (ENXIO); return (ENXIO);
} }
/* Initialize SPROM shadow */ /* Determine SPROM size */
if ((error = bhnd_sprom_init(&sc->shadow, sc->sprom_res, offset))) r_size = rman_get_size(r->res);
if (r_size <= offset || (r_size - offset) > BUS_SPACE_MAXSIZE) {
device_printf(dev, "invalid sprom offset\n");
error = ENXIO;
goto failed;
}
sprom_size = r_size - offset;
/* Allocate an I/O context for the SPROM parser. All SPROM reads
* must be 16-bit aligned */
io = bhnd_nvram_iores_new(r, offset, sprom_size, sizeof(uint16_t));
if (io == NULL) {
error = ENXIO;
goto failed;
}
/* Initialize NVRAM data store */
error = bhnd_nvram_store_parse_new(&sc->store, io,
&bhnd_nvram_sprom_class);
if (error)
goto failed; goto failed;
/* Initialize mutex */ /* Clean up our temporary I/O context and its backing resource */
SPROM_LOCK_INIT(sc); bhnd_nvram_io_free(io);
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
return (0); return (0);
failed: failed:
bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, /* Clean up I/O context before releasing its backing resource */
sc->sprom_res); if (io != NULL)
bhnd_nvram_io_free(io);
bhnd_release_resource(dev, SYS_RES_MEMORY, rid, r);
return (error); return (error);
} }
@ -157,10 +178,7 @@ bhnd_sprom_detach(device_t dev)
sc = device_get_softc(dev); sc = device_get_softc(dev);
bhnd_release_resource(dev, SYS_RES_MEMORY, sc->sprom_rid, bhnd_nvram_store_free(sc->store);
sc->sprom_res);
bhnd_sprom_fini(&sc->shadow);
SPROM_LOCK_DESTROY(sc);
return (0); return (0);
} }
@ -172,16 +190,9 @@ static int
bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len, bhnd_sprom_getvar_method(device_t dev, const char *name, void *buf, size_t *len,
bhnd_nvram_type type) bhnd_nvram_type type)
{ {
struct bhnd_sprom_softc *sc; struct bhnd_sprom_softc *sc = device_get_softc(dev);
int error;
sc = device_get_softc(dev); return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
SPROM_LOCK(sc);
error = bhnd_sprom_getvar(&sc->shadow, name, buf, len, type);
SPROM_UNLOCK(sc);
return (error);
} }
/** /**
@ -191,16 +202,9 @@ static int
bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf, bhnd_sprom_setvar_method(device_t dev, const char *name, const void *buf,
size_t len, bhnd_nvram_type type) size_t len, bhnd_nvram_type type)
{ {
struct bhnd_sprom_softc *sc; struct bhnd_sprom_softc *sc = device_get_softc(dev);
int error;
sc = device_get_softc(dev); return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
SPROM_LOCK(sc);
error = bhnd_sprom_setvar(&sc->shadow, name, buf, len, type);
SPROM_UNLOCK(sc);
return (error);
} }
static device_method_t bhnd_sprom_methods[] = { static device_method_t bhnd_sprom_methods[] = {
@ -218,5 +222,5 @@ static device_method_t bhnd_sprom_methods[] = {
DEVMETHOD_END DEVMETHOD_END
}; };
DEFINE_CLASS_0(bhnd_nvram, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc)); DEFINE_CLASS_0(bhnd_nvram_store, bhnd_sprom_driver, bhnd_sprom_methods, sizeof(struct bhnd_sprom_softc));
MODULE_VERSION(bhnd_sprom, 1); MODULE_VERSION(bhnd_sprom, 1);

View File

@ -1,756 +0,0 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/limits.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include "bhnd_nvram_common.h"
#include "bhnd_sprom_parservar.h"
/*
* BHND SPROM Parser
*
* Provides identification, decoding, and encoding of BHND SPROM data.
*/
static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset,
void *buf, size_t nbytes, uint8_t *crc);
static int sprom_extend_shadow(struct bhnd_sprom *sc,
size_t image_size, uint8_t *crc);
static int sprom_populate_shadow(struct bhnd_sprom *sc);
static int sprom_get_var_defn(struct bhnd_sprom *sc,
const char *name,
const struct bhnd_nvram_vardefn **var,
const struct bhnd_sprom_vardefn **sprom,
size_t *size, size_t *nelem,
bhnd_nvram_type req_type);
static char sprom_get_delim_char(struct bhnd_sprom *sc,
bhnd_nvram_sfmt sfmt);
/* SPROM revision is always located at the second-to-last byte */
#define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2)
/* SPROM CRC is always located at the last byte */
#define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc)
/* SPROM CRC covers all but the final CRC byte */
#define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1)
/* SPROM shadow I/O (with byte-order translation) */
#define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off)
#define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off))
#define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off))
#define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v))
#define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \
htole16(_v))
#define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \
htole32(_v))
/* SPROM shadow I/O (without byte-order translation) */
#define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off))
#define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off))
#define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off))
#define SPROM_WRITE_ENC_1(_sc, _off, _v) \
*((uint8_t *)((_sc)->sp_shadow + _off)) = (_v)
#define SPROM_WRITE_ENC_2(_sc, _off, _v) \
*((uint16_t *)((_sc)->sp_shadow + _off)) = (_v)
#define SPROM_WRITE_ENC_4(_sc, _off, _v) \
*((uint32_t *)((_sc)->sp_shadow + _off)) = (_v)
/* Call @p _next macro with the C type, widened (signed or unsigned) 32-bit C
* type, width, and min/max values associated with @p _dtype */
#define SPROM_SWITCH_TYPE(_dtype, _next, ...) \
do { \
switch (_dtype) { \
case BHND_NVRAM_TYPE_UINT8: \
_next (uint8_t, uint32_t, 1, 0, \
UINT8_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_UINT16: \
_next (uint16_t, uint32_t, 2, 0, \
UINT16_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_UINT32: \
_next (uint32_t, uint32_t, 4, 0, \
UINT32_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_INT8: \
_next (int8_t, int32_t, 1, \
INT8_MIN, INT8_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_INT16: \
_next (int16_t, int32_t, 2, \
INT16_MIN, INT16_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_INT32: \
_next (int32_t, int32_t, 4, \
INT32_MIN, INT32_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_CHAR: \
_next (char, int32_t, 1, \
CHAR_MIN, CHAR_MAX, ## __VA_ARGS__); \
break; \
case BHND_NVRAM_TYPE_CSTR: \
panic("%s: BHND_NVRAM_TYPE_CSTR unhandled", \
__FUNCTION__); \
break; \
} \
} while (0)
/* Verify the range of _val of (_stype) within _type */
#define SPROM_VERIFY_RANGE(_type, _widen, _width, _min, _max, _val, \
_stype) \
do { \
if (BHND_NVRAM_SIGNED_TYPE(_stype)) { \
int32_t sval = (int32_t) (_val); \
if (sval > (_max) || sval < (_min)) \
return (ERANGE); \
} else { \
if ((_val) > (_max)) \
return (ERANGE); \
} \
} while(0)
/*
* Table of supported SPROM image formats, sorted by image size, ascending.
*/
#define SPROM_FMT(_sz, _revmin, _revmax, _sig) \
{ SPROM_SZ_ ## _sz, _revmin, _revmax, \
SPROM_SIG_ ## _sig ## _OFF, \
SPROM_SIG_ ## _sig }
static const struct sprom_fmt {
size_t size;
uint8_t rev_min;
uint8_t rev_max;
size_t sig_offset;
uint16_t sig_req;
} sprom_fmts[] = {
SPROM_FMT(R1_3, 1, 3, NONE),
SPROM_FMT(R4_8_9, 4, 4, R4),
SPROM_FMT(R4_8_9, 8, 9, R8_9),
SPROM_FMT(R10, 10, 10, R10),
SPROM_FMT(R11, 11, 11, R11)
};
/**
* Identify the SPROM format at @p offset within @p r, verify the CRC,
* and allocate a local shadow copy of the SPROM data.
*
* After successful initialization, @p r will not be accessed; any pin
* configuration required for SPROM access may be reset.
*
* @param[out] sprom On success, will be initialized with shadow of the SPROM
* data.
* @param r An active resource mapping the SPROM data.
* @param offset Offset of the SPROM data within @p resource.
*/
int
bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r,
bus_size_t offset)
{
bus_size_t res_size;
int error;
sprom->dev = rman_get_device(r->res);
sprom->sp_res = r;
sprom->sp_res_off = offset;
/* Determine maximum possible SPROM image size */
res_size = rman_get_size(r->res);
if (offset >= res_size)
return (EINVAL);
sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX);
/* Allocate and populate SPROM shadow */
sprom->sp_size = 0;
sprom->sp_capacity = sprom->sp_size_max;
sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND_NVRAM, M_NOWAIT);
if (sprom->sp_shadow == NULL)
return (ENOMEM);
/* Read and identify SPROM image */
if ((error = sprom_populate_shadow(sprom)))
return (error);
return (0);
}
/**
* Release all resources held by @p sprom.
*
* @param sprom A SPROM instance previously initialized via bhnd_sprom_init().
*/
void
bhnd_sprom_fini(struct bhnd_sprom *sprom)
{
free(sprom->sp_shadow, M_BHND_NVRAM);
}
/* Perform a read using a SPROM offset descriptor, safely widening the result
* to its 32-bit representation before assigning it to @p _dest. */
#define SPROM_GETVAR_READ(_type, _widen, _width, _min, _max, _sc, _off, \
_dest) \
do { \
_type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \
if (_off->shift > 0) { \
_v >>= _off->shift; \
} else if (off->shift < 0) { \
_v <<= -_off->shift; \
} \
\
if (_off->cont) \
_dest |= ((uint32_t) (_widen) _v) & _off->mask; \
else \
_dest = ((uint32_t) (_widen) _v) & _off->mask; \
} while(0)
/* Emit a value read using a SPROM offset descriptor, narrowing the
* result output representation. */
#define SPROM_GETVAR_WRITE(_type, _widen, _width, _min, _max, _off, \
_src, _buf) \
do { \
_type _v = (_type) (_widen) _src; \
*((_type *)_buf) = _v; \
} while(0)
/* String format a value read using a SPROM offset descriptor */
#define SPROM_GETVAR_SNPRINTF(_type, _widen, _width, _min, _max, _src, \
_buf, _remain, _fmt, _nwrite) \
do { \
_nwrite = snprintf(_buf, _remain, _fmt, (_type) (_widen) _src); \
} while(0)
/**
* Read a SPROM variable, performing conversion to host byte order.
*
* @param sc The SPROM parser state.
* @param name The SPROM variable name.
* @param[out] buf On success, the requested value will be written
* to this buffer. This argment may be NULL if
* the value is not desired.
* @param[in,out] len The capacity of @p buf. On success, will be set
* to the actual size of the requested value.
* @param type The requested data type to be written to @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too
* small to hold the requested value.
* @retval non-zero If reading @p name otherwise fails, a regular unix
* error code will be returned.
*/
int
bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf,
size_t *len, bhnd_nvram_type type)
{
const struct bhnd_nvram_vardefn *nv;
const struct bhnd_sprom_vardefn *sv;
void *outp;
size_t all1_offs;
size_t req_size, nelem;
size_t str_remain;
char str_delim;
uint32_t val;
int error;
error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
if (error)
return (error);
outp = buf;
str_remain = 0;
str_delim = '\0';
if (type != BHND_NVRAM_TYPE_CSTR) {
/* Provide required size */
if (outp == NULL) {
*len = req_size;
return (0);
}
/* Check (and update) target buffer len */
if (*len < req_size)
return (ENOMEM);
else
*len = req_size;
} else {
/* String length calculation requires performing
* the actual string formatting */
KASSERT(req_size == 0,
("req_size set for variable-length type"));
/* If caller is querying length, the len argument
* may be uninitialized */
if (outp != NULL)
str_remain = *len;
/* Fetch delimiter for the variable's string format */
str_delim = sprom_get_delim_char(sc, nv->sfmt);
}
/* Read data */
all1_offs = 0;
val = 0;
for (size_t i = 0; i < sv->num_offsets; i++) {
const struct bhnd_sprom_offset *off;
off = &sv->offsets[i];
KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
/* If not a continuation, advance the output buffer; if
* a C string, this requires appending a delimiter character */
if (i > 0 && !off->cont) {
size_t width = bhnd_nvram_type_width(type);
/* Non-fixed width types (such as CSTR) will have a 0
* width value */
if (width != 0) {
KASSERT(outp != NULL, ("NULL output buffer"));
outp = ((uint8_t *)outp) + width;
}
/* Append CSTR delim, if necessary */
if (type == BHND_NVRAM_TYPE_CSTR &&
str_delim != '\0' &&
i != 0)
{
if (outp != NULL && str_remain >= 1) {
*((char *)outp) = str_delim;
outp = ((char *)outp + 1);
/* Drop outp reference if we hit 0 */
if (str_remain-- == 0)
outp = NULL;
}
if (SIZE_MAX - 1 < req_size)
return (EFTYPE); /* too long */
req_size++;
}
}
/* Read the value, widening to a common uint32
* representation */
SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val);
/* If IGNALL1, record whether value has all bits set. */
if (nv->flags & BHND_NVRAM_VF_IGNALL1) {
uint32_t all1;
all1 = off->mask;
if (off->shift > 0)
all1 >>= off->shift;
else if (off->shift < 0)
all1 <<= -off->shift;
if ((val & all1) == all1)
all1_offs++;
}
/* Skip writing if additional continuations remain */
if (i+1 < sv->num_offsets && sv->offsets[i].cont)
continue;
/* Perform write */
if (type == BHND_NVRAM_TYPE_CSTR) {
const char *fmtstr;
int written;
fmtstr = bhnd_nvram_type_fmt(off->type, nv->sfmt, i);
if (fmtstr == NULL) {
device_printf(sc->dev, "no NVRAM format string "
"for '%s' (type=%d)\n", name, off->type);
return (EOPNOTSUPP);
}
SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_SNPRINTF, val,
outp, str_remain, fmtstr, written);
if (written <= 0)
return (EFTYPE);
/* Calculate remaining capacity, drop outp reference
* if we hit 0 -- otherwise, advance the buffer
* position */
if (written >= str_remain) {
str_remain = 0;
outp = NULL;
} else {
str_remain -= written;
if (outp != NULL)
outp = (char *)outp + written;
}
/* Add additional bytes to total length */
if (SIZE_MAX - written < req_size)
return (EFTYPE); /* string too long */
req_size += written;
} else {
/* Verify range */
SPROM_SWITCH_TYPE(type, SPROM_VERIFY_RANGE, val,
off->type);
/* Write the value, narrowing to the appropriate output
* width. */
SPROM_SWITCH_TYPE(type, SPROM_GETVAR_WRITE, off, val,
outp);
}
}
/* Should value should be treated as uninitialized? */
if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets)
return (ENOENT);
/* If this is a C string request, we need to provide the computed
* length. */
if (type == BHND_NVRAM_TYPE_CSTR) {
/* Account for final trailing NUL */
if (SIZE_MAX - 1 < req_size)
return (EFTYPE); /* string too long */
req_size++;
/* Return an error if a too-small output buffer was provided */
if (buf != NULL && *len < req_size) {
*len = req_size;
return (ENOMEM);
}
*len = req_size;
}
return (0);
}
/* Perform a read of a variable offset from _src, safely widening the result
* to its 32-bit representation before assigning it to @p _dest. */
#define SPROM_SETVAR_READ(_type, _widen, _width, _min, _max, _off, \
_src, _dest) \
do { \
_type _v = *(const _type *)_src; \
if (_off->shift > 0) { \
_v <<= _off->shift; \
} else if (off->shift < 0) { \
_v >>= -_off->shift; \
} \
_dest = ((uint32_t) (_widen) _v) & _off->mask; \
} while(0)
/* Emit a value read using a SPROM offset descriptor, narrowing the
* result output representation and, if necessary, OR'ing it with the
* previously read value from @p _buf. */
#define SPROM_SETVAR_WRITE(_type, _widen, _width, _min, _max, _sc, \
_off, _src) \
do { \
_type _v = (_type) (_widen) _src; \
if (_off->cont) \
_v |= SPROM_READ_ ## _width(_sc, _off->offset); \
SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \
} while(0)
/**
* Set a local value for a SPROM variable, performing conversion to SPROM byte
* order.
*
* The new value will be written to the backing SPROM shadow.
*
* @param sc The SPROM parser state.
* @param name The SPROM variable name.
* @param[out] buf The new value.
* @param[in,out] len The size of @p buf.
* @param type The data type of @p buf.
*
* @retval 0 success
* @retval ENOENT The requested variable was not found.
* @retval EINVAL If @p len does not match the expected variable size.
*/
int
bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf,
size_t len, bhnd_nvram_type type)
{
const struct bhnd_nvram_vardefn *nv;
const struct bhnd_sprom_vardefn *sv;
size_t req_size, nelem;
int error;
uint8_t crc;
error = sprom_get_var_defn(sc, name, &nv, &sv, &req_size, &nelem, type);
if (error)
return (error);
/* String parsing is currently unsupported */
if (type == BHND_NVRAM_TYPE_CSTR)
return (EOPNOTSUPP);
/* Provide required size */
if (len != req_size)
return (EINVAL);
/* Write data */
for (size_t i = 0; i < sv->num_offsets; i++) {
const struct bhnd_sprom_offset *off;
uint32_t val;
off = &sv->offsets[i];
KASSERT(!off->cont || i > 0, ("cont marked on first offset"));
/* If not a continuation, advance the input pointer */
if (i > 0 && !off->cont) {
buf = ((const uint8_t *)buf) +
bhnd_nvram_type_width(sv->offsets[i-1].type);
}
/* Read the value, widening to a common uint32
* representation */
SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val);
/* Verify range */
SPROM_SWITCH_TYPE(nv->type, SPROM_VERIFY_RANGE, val, type);
/* Write the value, narrowing to the appropriate output
* width. */
SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val);
}
/* Update CRC */
crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc),
BHND_NVRAM_CRC8_INITIAL);
SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc);
return (0);
}
/* Read and identify the SPROM image by incrementally performing
* read + CRC of all supported image formats */
static int
sprom_populate_shadow(struct bhnd_sprom *sc)
{
const struct sprom_fmt *fmt;
int error;
uint16_t sig;
uint8_t srom_rev;
uint8_t crc;
crc = BHND_NVRAM_CRC8_INITIAL;
/* Identify the SPROM revision (and populate the SPROM shadow) */
for (size_t i = 0; i < nitems(sprom_fmts); i++) {
fmt = &sprom_fmts[i];
/* Read image data and check CRC */
if ((error = sprom_extend_shadow(sc, fmt->size, &crc)))
return (error);
/* Skip on invalid CRC */
if (crc != BHND_NVRAM_CRC8_VALID)
continue;
/* Fetch SROM revision */
srom_rev = SPROM_REV(sc);
/* Early sromrev 1 devices (specifically some BCM440x enet
* cards) are reported to have been incorrectly programmed
* with a revision of 0x10. */
if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10)
srom_rev = 0x1;
/* Verify revision range */
if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max)
continue;
/* Verify signature (if any) */
sig = SPROM_SIG_NONE;
if (fmt->sig_offset != SPROM_SIG_NONE_OFF)
sig = SPROM_READ_2(sc, fmt->sig_offset);
if (sig != fmt->sig_req) {
device_printf(sc->dev,
"invalid sprom %hhu signature: 0x%hx "
"(expected 0x%hx)\n",
srom_rev, sig, fmt->sig_req);
return (EINVAL);
}
/* Identified */
sc->sp_rev = srom_rev;
return (0);
}
/* identification failed */
device_printf(sc->dev, "unrecognized SPROM format\n");
return (EINVAL);
}
/*
* Extend the shadowed SPROM buffer to image_size, reading any required
* data from the backing SPROM resource and updating the CRC.
*/
static int
sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size,
uint8_t *crc)
{
int error;
KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported")));
/* Verify the request fits within our shadow buffer */
if (image_size > sc->sp_capacity)
return (ENOSPC);
/* Skip no-op requests */
if (sc->sp_size == image_size)
return (0);
/* Populate the extended range */
error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size,
image_size - sc->sp_size, crc);
if (error)
return (error);
sc->sp_size = image_size;
return (0);
}
/**
* Read nbytes at the given offset from the backing SPROM resource, and
* update the CRC.
*/
static int
sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf,
size_t nbytes, uint8_t *crc)
{
bus_size_t res_offset;
uint16_t *p;
KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size"));
KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset"));
/* Check for read overrun */
if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) {
device_printf(sc->dev, "requested SPROM read would overrun\n");
return (EINVAL);
}
/* Perform read and update CRC */
p = (uint16_t *)buf;
res_offset = sc->sp_res_off + offset;
bhnd_bus_read_region_stream_2(sc->sp_res, res_offset, p,
(nbytes / sizeof(uint16_t)));
*crc = bhnd_nvram_crc8(p, nbytes, *crc);
return (0);
}
/**
* Locate the variable and SPROM revision-specific definitions
* for variable with @p name.
*/
static int
sprom_get_var_defn(struct bhnd_sprom *sc, const char *name,
const struct bhnd_nvram_vardefn **var,
const struct bhnd_sprom_vardefn **sprom,
size_t *size, size_t *nelem, bhnd_nvram_type req_type)
{
/* Find variable definition */
*var = bhnd_nvram_find_vardefn(name);
if (*var == NULL)
return (ENOENT);
/* Find revision-specific SPROM definition */
for (size_t i = 0; i < (*var)->num_sp_defs; i++) {
const struct bhnd_sprom_vardefn *sp = &(*var)->sp_defs[i];
if (sc->sp_rev < sp->compat.first)
continue;
if (sc->sp_rev > sp->compat.last)
continue;
/* Found */
*sprom = sp;
/* Calculate element count and total size, in bytes */
*nelem = 0;
for (size_t j = 0; j < sp->num_offsets; j++)
if (!sp->offsets[j].cont)
*nelem += 1;
*size = bhnd_nvram_type_width(req_type) * (*nelem);
return (0);
}
/* Not supported by this SPROM revision */
return (ENOENT);
}
/**
* Return the array element delimiter for @p sfmt, or '\0' if none.
*/
static char
sprom_get_delim_char(struct bhnd_sprom *sc, bhnd_nvram_sfmt sfmt)
{
switch (sfmt) {
case BHND_NVRAM_SFMT_HEX:
case BHND_NVRAM_SFMT_DEC:
return (',');
case BHND_NVRAM_SFMT_CCODE:
case BHND_NVRAM_SFMT_LEDDC:
return ('\0');
case BHND_NVRAM_SFMT_MACADDR:
return (':');
default:
device_printf(sc->dev, "unknown NVRAM string format: %d\n",
sfmt);
return (',');
}
}

View File

@ -34,7 +34,7 @@
#include <dev/bhnd/bhnd.h> #include <dev/bhnd/bhnd.h>
#include "bhnd_sprom_parser.h" #include "bhnd_nvram_store.h"
DECLARE_CLASS(bhnd_sprom_driver); DECLARE_CLASS(bhnd_sprom_driver);
@ -49,11 +49,8 @@ int bhnd_sprom_detach(device_t dev);
* softc structures. * softc structures.
*/ */
struct bhnd_sprom_softc { struct bhnd_sprom_softc {
device_t dev; device_t dev;
struct bhnd_resource *sprom_res; /**< SPROM resource */ struct bhnd_nvram_store *store; /**< nvram store */
int sprom_rid; /**< SPROM RID */
struct bhnd_sprom shadow; /**< SPROM shadow */
struct mtx mtx; /**< SPROM shadow mutex */
}; };
#endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */ #endif /* _BHND_NVRAM_BHND_SPROMVAR_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -111,7 +111,7 @@ bwn_attach(device_t dev)
// TODO // TODO
uint8_t macaddr[6]; uint8_t macaddr[6];
error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr, error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr,
sizeof(macaddr), BHND_NVRAM_TYPE_UINT8); sizeof(macaddr), BHND_NVRAM_TYPE_UINT8_ARRAY);
if (error) if (error)
device_printf(dev, "error fetching macaddr: %d\n", error); device_printf(dev, "error fetching macaddr: %d\n", error);
else else

View File

@ -184,7 +184,7 @@ usb_process(void *arg)
continue; continue;
} }
/* end if messages - check if anyone is waiting for sync */ /* end of messages - check if anyone is waiting for sync */
if (up->up_dsleep) { if (up->up_dsleep) {
up->up_dsleep = 0; up->up_dsleep = 0;
cv_broadcast(&up->up_drain); cv_broadcast(&up->up_drain);

View File

@ -173,6 +173,8 @@ static void rsu_scan_end(struct ieee80211com *);
static void rsu_getradiocaps(struct ieee80211com *, int, int *, static void rsu_getradiocaps(struct ieee80211com *, int, int *,
struct ieee80211_channel[]); struct ieee80211_channel[]);
static void rsu_set_channel(struct ieee80211com *); static void rsu_set_channel(struct ieee80211com *);
static void rsu_scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void rsu_scan_mindwell(struct ieee80211_scan_state *);
static void rsu_update_mcast(struct ieee80211com *); static void rsu_update_mcast(struct ieee80211com *);
static int rsu_alloc_rx_list(struct rsu_softc *); static int rsu_alloc_rx_list(struct rsu_softc *);
static void rsu_free_rx_list(struct rsu_softc *); static void rsu_free_rx_list(struct rsu_softc *);
@ -203,7 +205,8 @@ static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void rsu_set_key(struct rsu_softc *, const struct ieee80211_key *); static void rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
static void rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *); static void rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
#endif #endif
static int rsu_site_survey(struct rsu_softc *, struct ieee80211vap *); static int rsu_site_survey(struct rsu_softc *,
struct ieee80211_scan_ssid *);
static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *); static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
static int rsu_disconnect(struct rsu_softc *); static int rsu_disconnect(struct rsu_softc *);
static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi); static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi);
@ -537,6 +540,7 @@ rsu_attach(device_t self)
ic->ic_txstream = sc->sc_ntxstream; ic->ic_txstream = sc->sc_ntxstream;
ic->ic_rxstream = sc->sc_nrxstream; ic->ic_rxstream = sc->sc_nrxstream;
} }
ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
ic->ic_channels); ic->ic_channels);
@ -547,6 +551,8 @@ rsu_attach(device_t self)
ic->ic_scan_end = rsu_scan_end; ic->ic_scan_end = rsu_scan_end;
ic->ic_getradiocaps = rsu_getradiocaps; ic->ic_getradiocaps = rsu_getradiocaps;
ic->ic_set_channel = rsu_set_channel; ic->ic_set_channel = rsu_set_channel;
ic->ic_scan_curchan = rsu_scan_curchan;
ic->ic_scan_mindwell = rsu_scan_mindwell;
ic->ic_vap_create = rsu_vap_create; ic->ic_vap_create = rsu_vap_create;
ic->ic_vap_delete = rsu_vap_delete; ic->ic_vap_delete = rsu_vap_delete;
ic->ic_update_mcast = rsu_update_mcast; ic->ic_update_mcast = rsu_update_mcast;
@ -680,16 +686,21 @@ static void
rsu_scan_start(struct ieee80211com *ic) rsu_scan_start(struct ieee80211com *ic)
{ {
struct rsu_softc *sc = ic->ic_softc; struct rsu_softc *sc = ic->ic_softc;
struct ieee80211_scan_state *ss = ic->ic_scan;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int error; int error;
/* Scanning is done by the firmware. */ /* Scanning is done by the firmware. */
RSU_LOCK(sc); RSU_LOCK(sc);
/* XXX TODO: force awake if in in network-sleep? */ sc->sc_active_scan = !!(ss->ss_flags & IEEE80211_SCAN_ACTIVE);
error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps)); /* XXX TODO: force awake if in network-sleep? */
error = rsu_site_survey(sc, ss->ss_nssid > 0 ? &ss->ss_ssid[0] : NULL);
RSU_UNLOCK(sc); RSU_UNLOCK(sc);
if (error != 0) if (error != 0) {
device_printf(sc->sc_dev, device_printf(sc->sc_dev,
"could not send site survey command\n"); "could not send site survey command\n");
ieee80211_cancel_scan(vap);
}
} }
static void static void
@ -721,6 +732,24 @@ rsu_set_channel(struct ieee80211com *ic __unused)
/* We are unable to switch channels, yet. */ /* We are unable to switch channels, yet. */
} }
static void
rsu_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* Scan is done in rsu_scan_start(). */
}
/**
* Called by the net80211 framework to indicate
* the minimum dwell time has been met, terminate the scan.
* We don't actually terminate the scan as the firmware will notify
* us when it's finished and we have no way to interrupt it.
*/
static void
rsu_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
static void static void
rsu_update_mcast(struct ieee80211com *ic) rsu_update_mcast(struct ieee80211com *ic)
{ {
@ -1323,31 +1352,36 @@ rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
#endif #endif
static int static int
rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap) rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid)
{ {
struct r92s_fw_cmd_sitesurvey cmd; struct r92s_fw_cmd_sitesurvey cmd;
struct ieee80211com *ic = &sc->sc_ic;
int r;
RSU_ASSERT_LOCKED(sc); RSU_ASSERT_LOCKED(sc);
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->sc_scan_pass == 1) /* TODO: passive channels? */
if (sc->sc_active_scan)
cmd.active = htole32(1); cmd.active = htole32(1);
cmd.limit = htole32(48); cmd.limit = htole32(48);
if (sc->sc_scan_pass == 1 && vap->iv_des_nssid > 0) {
/* Do a directed scan for second pass. */ if (ssid != NULL) {
cmd.ssidlen = htole32(vap->iv_des_ssid[0].len); sc->sc_extra_scan = 1;
memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid, cmd.ssidlen = htole32(ssid->len);
vap->iv_des_ssid[0].len); memcpy(cmd.ssid, ssid->ssid, ssid->len);
} }
DPRINTF("sending site survey command, pass=%d\n", sc->sc_scan_pass); #ifdef USB_DEBUG
r = rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)); if (rsu_debug & (RSU_DEBUG_SCAN | RSU_DEBUG_FWCMD)) {
if (r == 0) { device_printf(sc->sc_dev,
sc->sc_scanning = 1; "sending site survey command, active %d",
le32toh(cmd.active));
if (ssid != NULL) {
printf(", ssid: ");
ieee80211_print_essid(cmd.ssid, le32toh(cmd.ssidlen));
}
printf("\n");
} }
return (r); #endif
return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
} }
static int static int
@ -1362,28 +1396,9 @@ rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
uint8_t *frm; uint8_t *frm;
uint8_t opmode; uint8_t opmode;
int error; int error;
int cnt;
char *msg = "rsujoin";
RSU_ASSERT_LOCKED(sc); RSU_ASSERT_LOCKED(sc);
/*
* Until net80211 scanning doesn't automatically finish
* before we tell it to, let's just wait until any pending
* scan is done.
*
* XXX TODO: yes, this releases and re-acquires the lock.
* We should re-verify the state whenever we re-attempt this!
*/
cnt = 0;
while (sc->sc_scanning && cnt < 10) {
device_printf(sc->sc_dev,
"%s: still scanning! (attempt %d)\n",
__func__, cnt);
msleep(msg, &sc->sc_mtx, 0, msg, hz / 2);
cnt++;
}
/* Let the FW decide the opmode based on the capinfo field. */ /* Let the FW decide the opmode based on the capinfo field. */
opmode = NDIS802_11AUTOUNKNOWN; opmode = NDIS802_11AUTOUNKNOWN;
RSU_DPRINTF(sc, RSU_DEBUG_RESET, RSU_DPRINTF(sc, RSU_DEBUG_RESET,
@ -1634,26 +1649,24 @@ rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
break; break;
case R92S_EVT_SURVEY_DONE: case R92S_EVT_SURVEY_DONE:
RSU_DPRINTF(sc, RSU_DEBUG_SCAN, RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
"%s: site survey pass %d done, found %d BSS\n", "%s: %s scan done, found %d BSS\n",
__func__, sc->sc_scan_pass, le32toh(*(uint32_t *)buf)); __func__, sc->sc_extra_scan ? "direct" : "broadcast",
sc->sc_scanning = 0; le32toh(*(uint32_t *)buf));
if (vap->iv_state != IEEE80211_S_SCAN) if (sc->sc_extra_scan == 1) {
break; /* Ignore if not scanning. */ /* Send broadcast probe request. */
sc->sc_extra_scan = 0;
/* if (vap != NULL && rsu_site_survey(sc, NULL) != 0) {
* XXX TODO: This needs to be done without a transition to RSU_UNLOCK(sc);
* the SCAN state again. Grr. ieee80211_cancel_scan(vap);
*/ RSU_LOCK(sc);
if (sc->sc_scan_pass == 0 && vap->iv_des_nssid != 0) { }
/* Schedule a directed scan for hidden APs. */
/* XXX bad! */
sc->sc_scan_pass = 1;
RSU_UNLOCK(sc);
ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
RSU_LOCK(sc);
break; break;
} }
sc->sc_scan_pass = 0; if (vap != NULL) {
RSU_UNLOCK(sc);
ieee80211_scan_done(vap);
RSU_LOCK(sc);
}
break; break;
case R92S_EVT_JOIN_BSS: case R92S_EVT_JOIN_BSS:
if (vap->iv_state == IEEE80211_S_AUTH) if (vap->iv_state == IEEE80211_S_AUTH)
@ -2920,12 +2933,11 @@ rsu_init(struct rsu_softc *sc)
goto fail; goto fail;
} }
sc->sc_scan_pass = 0; sc->sc_extra_scan = 0;
usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]); usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
/* We're ready to go. */ /* We're ready to go. */
sc->sc_running = 1; sc->sc_running = 1;
sc->sc_scanning = 0;
return; return;
fail: fail:
/* Need to stop all failed transfers, if any */ /* Need to stop all failed transfers, if any */

View File

@ -573,7 +573,7 @@ struct r92s_tx_desc {
#define R92S_TXDW1_QSEL_M 0x00001f00 #define R92S_TXDW1_QSEL_M 0x00001f00
#define R92S_TXDW1_QSEL_S 8 #define R92S_TXDW1_QSEL_S 8
#define R92S_TXDW1_QSEL_BE 0x03 #define R92S_TXDW1_QSEL_BE 0x03
#define R92S_TXDW1_QSEL_H2C 0x1f #define R92S_TXDW1_QSEL_H2C 0x13
#define R92S_TXDW1_NONQOS 0x00010000 #define R92S_TXDW1_NONQOS 0x00010000
#define R92S_TXDW1_KEYIDX_M 0x00060000 #define R92S_TXDW1_KEYIDX_M 0x00060000
#define R92S_TXDW1_KEYIDX_S 17 #define R92S_TXDW1_KEYIDX_S 17
@ -768,8 +768,8 @@ struct rsu_softc {
u_int sc_running:1, u_int sc_running:1,
sc_calibrating:1, sc_calibrating:1,
sc_scanning:1, sc_active_scan:1,
sc_scan_pass:1; sc_extra_scan:1;
u_int cut; u_int cut;
uint8_t sc_rftype; uint8_t sc_rftype;
int8_t sc_nrxstream; int8_t sc_nrxstream;

View File

@ -537,7 +537,8 @@ ext2_mountfs(struct vnode *devvp, struct mount *mp)
struct csum *sump; struct csum *sump;
int error; int error;
int ronly; int ronly;
int i, size; int i;
u_long size;
int32_t *lp; int32_t *lp;
int32_t e2fs_maxcontig; int32_t e2fs_maxcontig;

View File

@ -608,21 +608,6 @@ nfssvc_call(struct thread *p, struct nfssvc_args *uap, struct ucred *cred)
nfsstatsv1.srvcache_nonidemdonehits = 0; nfsstatsv1.srvcache_nonidemdonehits = 0;
nfsstatsv1.srvcache_misses = 0; nfsstatsv1.srvcache_misses = 0;
nfsstatsv1.srvcache_tcppeak = 0; nfsstatsv1.srvcache_tcppeak = 0;
nfsstatsv1.srvclients = 0;
nfsstatsv1.srvopenowners = 0;
nfsstatsv1.srvopens = 0;
nfsstatsv1.srvlockowners = 0;
nfsstatsv1.srvlocks = 0;
nfsstatsv1.srvdelegates = 0;
nfsstatsv1.clopenowners = 0;
nfsstatsv1.clopens = 0;
nfsstatsv1.cllockowners = 0;
nfsstatsv1.cllocks = 0;
nfsstatsv1.cldelegates = 0;
nfsstatsv1.cllocalopenowners = 0;
nfsstatsv1.cllocalopens = 0;
nfsstatsv1.cllocallockowners = 0;
nfsstatsv1.cllocallocks = 0;
bzero(nfsstatsv1.srvrpccnt, bzero(nfsstatsv1.srvrpccnt,
sizeof(nfsstatsv1.srvrpccnt)); sizeof(nfsstatsv1.srvrpccnt));
bzero(nfsstatsv1.cbrpccnt, bzero(nfsstatsv1.cbrpccnt,

View File

@ -1320,6 +1320,8 @@ nfs_mount(struct mount *mp)
MNT_ILOCK(mp); MNT_ILOCK(mp);
mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF | mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF |
MNTK_USES_BCACHE; MNTK_USES_BCACHE;
if ((VFSTONFS(mp)->nm_flag & NFSMNT_NFSV4) != 0)
mp->mnt_kern_flag |= MNTK_NULL_NOCACHE;
MNT_IUNLOCK(mp); MNT_IUNLOCK(mp);
} }
return (error); return (error);

View File

@ -188,7 +188,8 @@ nullfs_mount(struct mount *mp)
} }
xmp->nullm_flags |= NULLM_CACHE; xmp->nullm_flags |= NULLM_CACHE;
if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0) if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 ||
(xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0)
xmp->nullm_flags &= ~NULLM_CACHE; xmp->nullm_flags &= ~NULLM_CACHE;
MNT_ILOCK(mp); MNT_ILOCK(mp);

View File

@ -3002,7 +3002,7 @@ vinactive(struct vnode *vp, struct thread *td)
obj = vp->v_object; obj = vp->v_object;
if (obj != NULL && (obj->flags & OBJ_MIGHTBEDIRTY) != 0) { if (obj != NULL && (obj->flags & OBJ_MIGHTBEDIRTY) != 0) {
VM_OBJECT_WLOCK(obj); VM_OBJECT_WLOCK(obj);
vm_object_page_clean(obj, 0, 0, OBJPC_NOSYNC); vm_object_page_clean(obj, 0, 0, 0);
VM_OBJECT_WUNLOCK(obj); VM_OBJECT_WUNLOCK(obj);
} }
VOP_INACTIVE(vp, td); VOP_INACTIVE(vp, td);

View File

@ -0,0 +1,527 @@
/*-
* Copyright (c) 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* BHND CFE NVRAM driver.
*
* Provides access to device NVRAM via CFE.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/limits.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/bhnd/bhnd.h>
#include <dev/cfe/cfe_api.h>
#include <dev/cfe/cfe_error.h>
#include <dev/cfe/cfe_ioctl.h>
#include <dev/bhnd/nvram/bhnd_nvram_iovar.h>
#include "bhnd_nvram_if.h"
#include "bcm_nvram_cfevar.h"
/**
* CFE-backed bhnd_nvram_io implementation.
*/
struct bhnd_nvram_iocfe {
struct bhnd_nvram_io io; /**< common I/O instance state */
char *dname; /**< CFE device name (borrowed) */
int fd; /**< CFE file descriptor */
size_t offset; /**< base offset */
size_t size; /**< device size */
bool req_blk_erase; /**< flash blocks must be erased
before writing */
};
BHND_NVRAM_IOPS_DEFN(iocfe)
#define IOCFE_LOG(_io, _fmt, ...) \
printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__)
static int bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io,
char *dname);
static struct bhnd_nvram_io *bhnd_nvram_find_cfedev(device_t dev,
char **dname,
bhnd_nvram_data_class_t **cls);
/** Known CFE NVRAM device names, in probe order. */
static char *nvram_cfe_devs[] = {
"nflash0.nvram", /* NAND */
"nflash1.nvram",
"flash0.nvram",
"flash1.nvram",
};
/** Supported CFE NVRAM formats, in probe order. */
static bhnd_nvram_data_class_t * const nvram_cfe_fmts[] = {
&bhnd_nvram_bcm_class,
&bhnd_nvram_tlv_class
};
static int
bhnd_nvram_cfe_probe(device_t dev)
{
struct bhnd_nvram_io *io;
bhnd_nvram_data_class_t *cls;
const char *cls_desc;
char *dname;
char *desc;
/* Locate a usable CFE device */
io = bhnd_nvram_find_cfedev(dev, &dname, &cls);
if (io == NULL)
return (ENXIO);
bhnd_nvram_io_free(io);
/* Format the device description */
cls_desc = bhnd_nvram_data_class_desc(cls);
asprintf(&desc, M_DEVBUF, "%s CFE %s", cls_desc, dname);
if (desc != NULL) {
device_set_desc_copy(dev, desc);
free(desc, M_DEVBUF);
} else {
device_set_desc(dev, cls_desc);
}
/* Refuse wildcard attachments */
return (BUS_PROBE_NOWILDCARD);
}
static int
bhnd_nvram_cfe_attach(device_t dev)
{
struct bhnd_nvram_cfe_softc *sc;
bhnd_nvram_data_class_t *cls;
struct bhnd_nvram_io *io;
char *dname;
int error;
sc = device_get_softc(dev);
sc->dev = dev;
/* Locate NVRAM device via CFE */
io = bhnd_nvram_find_cfedev(dev, &dname, &cls);
if (io == NULL) {
device_printf(dev, "CFE NVRAM device not found\n");
return (ENXIO);
}
/* Initialize NVRAM store and free the I/O context */
error = bhnd_nvram_store_parse_new(&sc->store, io, cls);
bhnd_nvram_io_free(io);
if (error)
return (error);
return (error);
}
static int
bhnd_nvram_cfe_resume(device_t dev)
{
return (0);
}
static int
bhnd_nvram_cfe_suspend(device_t dev)
{
return (0);
}
static int
bhnd_nvram_cfe_detach(device_t dev)
{
struct bhnd_nvram_cfe_softc *sc;
sc = device_get_softc(dev);
bhnd_nvram_store_free(sc->store);
return (0);
}
static int
bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
bhnd_nvram_type type)
{
struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
}
static int
bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
size_t len, bhnd_nvram_type type)
{
struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
}
/**
* Find, open, identify, and return an I/O context mapping our
* CFE NVRAM device.
*
* @param dev bhnd_nvram_cfe device.
* @param[out] dname On success, the CFE device name.
* @param[out] cls On success, the identified NVRAM data format
* class.
*
* @retval non-NULL success. the caller inherits ownership of the returned
* NVRAM I/O context.
* @retval NULL if no usable CFE NVRAM device could be found.
*/
static struct bhnd_nvram_io *
bhnd_nvram_find_cfedev(device_t dev, char **dname,
bhnd_nvram_data_class_t **cls)
{
struct bhnd_nvram_io *io;
int devinfo;
int error, result;
for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
*cls = nvram_cfe_fmts[i];
for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
*dname = nvram_cfe_devs[j];
/* Does the device exist? */
if ((devinfo = cfe_getdevinfo(*dname)) < 0) {
if (devinfo != CFE_ERR_DEVNOTFOUND) {
device_printf(dev, "cfe_getdevinfo(%s) "
"failed: %d\n", *dname, devinfo);
}
continue;
}
/* Open for reading */
if ((error = bhnd_nvram_iocfe_new(&io, *dname)))
continue;
/* Probe */
result = bhnd_nvram_data_probe(*cls, io);
if (result <= 0) {
/* Found a supporting NVRAM data class */
return (io);
}
/* Keep searching */
bhnd_nvram_io_free(io);
io = NULL;
}
}
return (NULL);
}
/**
* Allocate and return a new I/O context backed by a CFE device.
*
* The caller is responsible for deallocating the returned I/O context via
* bhnd_nvram_io_free().
*
* @param[out] io On success, a valid I/O context for @p dname.
* @param dname The name of the CFE device to be opened for reading.
*
* @retval 0 success.
* @retval non-zero if opening @p dname otherwise fails, a standard unix error
* will be returned.
*/
static int
bhnd_nvram_iocfe_new(struct bhnd_nvram_io **io, char *dname)
{
struct bhnd_nvram_iocfe *iocfe;
nvram_info_t nvram_info;
int cerr, devinfo, dtype, rlen;
int64_t nv_offset;
u_int nv_size;
bool req_blk_erase;
int error;
iocfe = malloc(sizeof(*iocfe), M_DEVBUF, M_WAITOK);
iocfe->io.iops = &bhnd_nvram_iocfe_ops;
iocfe->dname = dname;
/* Try to open the device */
iocfe->fd = cfe_open(dname);
if (iocfe->fd <= 0) {
IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
error = ENXIO;
goto failed;
}
/* Try to fetch device info */
if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
error = ENXIO;
goto failed;
}
/* Verify device type */
dtype = devinfo & CFE_DEV_MASK;
switch (dtype) {
case CFE_DEV_FLASH:
case CFE_DEV_NVRAM:
/* Valid device type */
break;
default:
IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
error = ENXIO;
goto failed;
}
/* Try to fetch nvram info from CFE */
cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
(unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
if (cerr == CFE_OK) {
/* Sanity check the result; must not be a negative integer */
if (nvram_info.nvram_size < 0 ||
nvram_info.nvram_offset < 0)
{
IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
nvram_info.nvram_size, nvram_info.nvram_offset);
error = ENXIO;
goto failed;
}
nv_offset = nvram_info.nvram_offset;
nv_size = nvram_info.nvram_size;
req_blk_erase = (nvram_info.nvram_eraseflg != 0);
} else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
error = ENXIO;
goto failed;
}
/* Fall back on flash info.
*
* This is known to be required on the Asus RT-N53 (CFE 5.70.55.33,
* BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
* CFE_ERR_INV_COMMAND.
*/
if (cerr == CFE_ERR_INV_COMMAND) {
flash_info_t fi;
cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
(unsigned char *)&fi, sizeof(fi), &rlen, 0);
if (cerr != CFE_OK) {
IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
cerr);
error = ENXIO;
goto failed;
}
nv_offset = 0x0;
nv_size = fi.flash_size;
req_blk_erase = !(fi.flash_flags & FLASH_FLAG_NOERASE);
}
/* Verify that the full NVRAM layout can be represented via size_t */
if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
nv_size, (intmax_t)nv_offset);
error = ENXIO;
goto failed;
}
iocfe->offset = nv_offset;
iocfe->size = nv_size;
iocfe->req_blk_erase = req_blk_erase;
*io = &iocfe->io;
return (CFE_OK);
failed:
if (iocfe->fd >= 0)
cfe_close(iocfe->fd);
free(iocfe, M_DEVBUF);
*io = NULL;
return (error);
}
static void
bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io;
cfe_close(iocfe->fd);
free(io, M_DEVBUF);
}
static size_t
bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iocfe *iocfe = (struct bhnd_nvram_iocfe *)io;
return (iocfe->size);
}
static int
bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
void **ptr, size_t nbytes, size_t *navail)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
/* unsupported */
return (ENODEV);
}
static int
bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
struct bhnd_nvram_iocfe *iocfe;
size_t remain;
int64_t cfe_offset;
int nr, nreq;
iocfe = (struct bhnd_nvram_iocfe *)io;
/* Determine (and validate) the base CFE offset */
#if (SIZE_MAX > INT64_MAX)
if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
return (ENXIO);
#endif
if (INT64_MAX - offset < iocfe->offset)
return (ENXIO);
cfe_offset = iocfe->offset + offset;
/* Verify that cfe_offset + nbytes is representable */
if (INT64_MAX - cfe_offset < nbytes)
return (ENXIO);
/* Perform the read */
for (remain = nbytes; remain > 0;) {
void *p;
size_t nread;
int64_t cfe_noff;
nread = (nbytes - remain);
cfe_noff = cfe_offset + nread;
p = ((uint8_t *)buffer + nread);
nreq = ummin(INT_MAX, remain);
nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
if (nr < 0) {
IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
return (ENXIO);
}
/* Check for unexpected short read */
if (nr == 0 && remain > 0) {
/* If the request fits entirely within the CFE
* device range, we shouldn't hit EOF */
if (remain < iocfe->size &&
iocfe->size - remain > offset)
{
IOCFE_LOG(iocfe, "cfe_readblk() returned "
"unexpected short read (%d/%d)\n", nr,
nreq);
return (ENXIO);
}
}
if (nr == 0)
break;
remain -= nr;
}
/* Check for short read */
if (remain > 0)
return (ENXIO);
return (0);
}
static device_method_t bhnd_nvram_cfe_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bhnd_nvram_cfe_probe),
DEVMETHOD(device_attach, bhnd_nvram_cfe_attach),
DEVMETHOD(device_resume, bhnd_nvram_cfe_resume),
DEVMETHOD(device_suspend, bhnd_nvram_cfe_suspend),
DEVMETHOD(device_detach, bhnd_nvram_cfe_detach),
/* NVRAM interface */
DEVMETHOD(bhnd_nvram_getvar, bhnd_nvram_cfe_getvar),
DEVMETHOD(bhnd_nvram_setvar, bhnd_nvram_cfe_setvar),
DEVMETHOD_END
};
DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
sizeof(struct bhnd_nvram_cfe_softc));
EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -29,36 +29,19 @@
* $FreeBSD$ * $FreeBSD$
*/ */
#ifndef _BHND_NVRAM_BHND_SPROM_PARSER_H_ #ifndef _MIPS_BROADCOM_BCM_NVRAM_CFE_H_
#define _BHND_NVRAM_BHND_SPROM_PARSER_H_ #define _MIPS_BROADCOM_BCM_NVRAM_CFE_H_
#include <dev/bhnd/bhnd.h> #include <sys/param.h>
#include <sys/bus.h>
struct bhnd_sprom; #include <dev/bhnd/nvram/bhnd_nvram.h>
#include <dev/bhnd/nvram/bhnd_nvram_store.h>
int bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, /** bhnd_nvram_cfe driver instance state. */
bus_size_t offset); struct bhnd_nvram_cfe_softc {
void bhnd_sprom_fini(struct bhnd_sprom *sprom); device_t dev;
int bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, struct bhnd_nvram_store *store; /**< nvram store */
size_t *len, bhnd_nvram_type type);
int bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name,
const void *buf, size_t len, bhnd_nvram_type type);
/**
* bhnd sprom parser instance state.
*/
struct bhnd_sprom {
device_t dev; /**< sprom parent device */
uint8_t sp_rev; /**< sprom revision */
struct bhnd_resource *sp_res; /**< sprom resource. */
bus_size_t sp_res_off; /**< offset to sprom image */
uint8_t *sp_shadow; /**< sprom shadow */
bus_size_t sp_size_max; /**< maximum possible sprom length */
size_t sp_size; /**< shadow size */
size_t sp_capacity; /**< shadow buffer capacity */
}; };
#endif /* _BHND_NVRAM_BHND_SPROM_PARSER_H_ */ #endif /* _MIPS_BROADCOM_BCM_NVRAM_CFE_H_ */

View File

@ -7,6 +7,8 @@
mips/broadcom/bcm_machdep.c standard mips/broadcom/bcm_machdep.c standard
mips/broadcom/bcm_bmips.c optional siba_nexus siba mips/broadcom/bcm_bmips.c optional siba_nexus siba
mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma mips/broadcom/bcm_mips74k.c optional bcma_nexus bcma
mips/broadcom/bcm_nvram_cfe.c optional bhnd siba_nexus cfe | \
bhnd bcma_nexus cfe
mips/broadcom/bcm_pmu.c standard mips/broadcom/bcm_pmu.c standard
mips/mips/tick.c standard mips/mips/tick.c standard

View File

@ -2,6 +2,8 @@
# #
# $FreeBSD$ # $FreeBSD$
#NO_UNIVERSE
ident JZ4780 ident JZ4780
machine mips mipsel machine mips mipsel
cpu CPU_XBURST cpu CPU_XBURST

View File

@ -2,6 +2,8 @@
# #
# $FreeBSD$ # $FreeBSD$
#NO_UNIVERSE
ident X1000 ident X1000
machine mips mipsel machine mips mipsel
cpu CPU_XBURST cpu CPU_XBURST

View File

@ -27,11 +27,21 @@ SRCS+= bhnd_pmu.c \
SRCS+= bhnd_pmu_if.c bhnd_pmu_if.h SRCS+= bhnd_pmu_if.c bhnd_pmu_if.h
# NVRAM/SPROM # NVRAM/SPROM
SRCS+= bhnd_nvram.c \ SRCS+= bhnd_nvram_data.c \
bhnd_nvram_parser.c \ bhnd_nvram_data_bcm.c \
bhnd_sprom.c \ bhnd_nvram_data_bcmraw.c \
bhnd_sprom_parser.c bhnd_nvram_data_btxt.c \
SRCS+= bhnd_nvram_common.c bhnd_nvram_data_sprom.c \
bhnd_nvram_data_tlv.c \
bhnd_nvram_io.c \
bhnd_nvram_iobuf.c \
bhnd_nvram_iores.c \
bhnd_nvram_store.c \
bhnd_nvram_subr.c \
bhnd_nvram_value.c \
bhnd_nvram_value_fmts.c \
bhnd_nvram_value_prf.c \
bhnd_sprom.c
SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h SRCS+= bhnd_nvram_map.h bhnd_nvram_map_data.h
SRCS+= bhnd_nvram_if.c bhnd_nvram_if.h SRCS+= bhnd_nvram_if.c bhnd_nvram_if.h

View File

@ -39,8 +39,10 @@
#include <sys/param.h> #include <sys/param.h>
#ifdef _KERNEL #ifdef _KERNEL
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/lock.h>
#include <sys/malloc.h> #include <sys/malloc.h>
#include <sys/mbuf.h> #include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/queue.h> #include <sys/queue.h>
#endif #endif
#include <sys/socket.h> #include <sys/socket.h>

View File

@ -35,6 +35,7 @@
#ifdef _KERNEL #ifdef _KERNEL
#include <sys/lock.h>
#include <sys/mutex.h> #include <sys/mutex.h>
#include <netipsec/key_var.h> #include <netipsec/key_var.h>

View File

@ -1256,9 +1256,9 @@ nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport,
*/ */
mtu -= sizeof(struct ip6_hdr) - sizeof(struct ip); mtu -= sizeof(struct ip6_hdr) - sizeof(struct ip);
break; break;
case ICMP6_TIME_EXCEED_TRANSIT: case ICMP6_TIME_EXCEEDED:
type = ICMP_TIMXCEED; type = ICMP_TIMXCEED;
code = ICMP_TIMXCEED_INTRANS; code = icmp6->icmp6_code;
break; break;
case ICMP6_PARAM_PROB: case ICMP6_PARAM_PROB:
switch (icmp6->icmp6_code) { switch (icmp6->icmp6_code) {

View File

@ -370,7 +370,8 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
#define MNTK_SUSPEND 0x08000000 /* request write suspension */ #define MNTK_SUSPEND 0x08000000 /* request write suspension */
#define MNTK_SUSPEND2 0x04000000 /* block secondary writes */ #define MNTK_SUSPEND2 0x04000000 /* block secondary writes */
#define MNTK_SUSPENDED 0x10000000 /* write operations are suspended */ #define MNTK_SUSPENDED 0x10000000 /* write operations are suspended */
#define MNTK_UNUSED1 0x20000000 #define MNTK_NULL_NOCACHE 0x20000000 /* auto disable cache for nullfs
mounts over this fs */
#define MNTK_LOOKUP_SHARED 0x40000000 /* FS supports shared lock lookups */ #define MNTK_LOOKUP_SHARED 0x40000000 /* FS supports shared lock lookups */
#define MNTK_NOKNOTE 0x80000000 /* Don't send KNOTEs from VOP hooks */ #define MNTK_NOKNOTE 0x80000000 /* Don't send KNOTEs from VOP hooks */

View File

@ -1512,19 +1512,17 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req)
} }
/* /*
* The page allocation request can came from consumers which already * Allocate a page if the number of free pages exceeds the minimum
* hold the free page queue mutex, like vm_page_insert() in * for the request class.
* vm_page_cache().
*/ */
mtx_lock_flags(&vm_page_queue_free_mtx, MTX_RECURSE); mtx_lock(&vm_page_queue_free_mtx);
if (vm_cnt.v_free_count > vm_cnt.v_free_reserved || if (vm_cnt.v_free_count > vm_cnt.v_free_reserved ||
(req_class == VM_ALLOC_SYSTEM && (req_class == VM_ALLOC_SYSTEM &&
vm_cnt.v_free_count > vm_cnt.v_interrupt_free_min) || vm_cnt.v_free_count > vm_cnt.v_interrupt_free_min) ||
(req_class == VM_ALLOC_INTERRUPT && (req_class == VM_ALLOC_INTERRUPT &&
vm_cnt.v_free_count > 0)) { vm_cnt.v_free_count > 0)) {
/* /*
* Allocate from the free queue if the number of free pages * Can we allocate the page from a reservation?
* exceeds the minimum for the request class.
*/ */
#if VM_NRESERVLEVEL > 0 #if VM_NRESERVLEVEL > 0
if (object == NULL || (object->flags & (OBJ_COLORED | if (object == NULL || (object->flags & (OBJ_COLORED |
@ -1532,6 +1530,9 @@ vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int req)
vm_reserv_alloc_page(object, pindex, mpred)) == NULL) vm_reserv_alloc_page(object, pindex, mpred)) == NULL)
#endif #endif
{ {
/*
* If not, allocate it from the free page queues.
*/
m = vm_phys_alloc_pages(object != NULL ? m = vm_phys_alloc_pages(object != NULL ?
VM_FREEPOOL_DEFAULT : VM_FREEPOOL_DIRECT, 0); VM_FREEPOOL_DEFAULT : VM_FREEPOOL_DIRECT, 0);
#if VM_NRESERVLEVEL > 0 #if VM_NRESERVLEVEL > 0
@ -1841,7 +1842,7 @@ vm_page_alloc_freelist(int flind, int req)
/* /*
* Do not allocate reserved pages unless the req has asked for it. * Do not allocate reserved pages unless the req has asked for it.
*/ */
mtx_lock_flags(&vm_page_queue_free_mtx, MTX_RECURSE); mtx_lock(&vm_page_queue_free_mtx);
if (vm_cnt.v_free_count > vm_cnt.v_free_reserved || if (vm_cnt.v_free_count > vm_cnt.v_free_reserved ||
(req_class == VM_ALLOC_SYSTEM && (req_class == VM_ALLOC_SYSTEM &&
vm_cnt.v_free_count > vm_cnt.v_interrupt_free_min) || vm_cnt.v_free_count > vm_cnt.v_interrupt_free_min) ||

View File

@ -478,8 +478,9 @@ native_lapic_init(vm_paddr_t addr)
lapic_et.et_quality = 600; lapic_et.et_quality = 600;
if (!arat) { if (!arat) {
lapic_et.et_flags |= ET_FLAGS_C3STOP; lapic_et.et_flags |= ET_FLAGS_C3STOP;
lapic_et.et_quality -= 200; lapic_et.et_quality = 100;
} else if ((cpu_feature & CPUID_TSC) != 0 && }
if ((cpu_feature & CPUID_TSC) != 0 &&
(cpu_feature2 & CPUID2_TSCDLT) != 0 && (cpu_feature2 & CPUID2_TSCDLT) != 0 &&
tsc_is_invariant && tsc_freq != 0) { tsc_is_invariant && tsc_freq != 0) {
lapic_timer_tsc_deadline = 1; lapic_timer_tsc_deadline = 1;

View File

@ -1142,6 +1142,7 @@ OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/asan_interface.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/common_interface_defs.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/common_interface_defs.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/coverage_interface.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/coverage_interface.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/dfsan_interface.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/dfsan_interface.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/esan_interface.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/linux_syscall_hooks.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/linux_syscall_hooks.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/lsan_interface.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/lsan_interface.h
OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/msan_interface.h OLD_FILES+=usr/lib/clang/3.9.1/include/sanitizer/msan_interface.h
@ -1233,6 +1234,10 @@ OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.profile-i386.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.profile-x86_64.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.profile-x86_64.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.safestack-i386.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.safestack-i386.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.safestack-x86_64.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.safestack-x86_64.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.stats-i386.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.stats-x86_64.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.stats_client-i386.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.stats_client-x86_64.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone-i386.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone-i386.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone-x86_64.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone-x86_64.a
OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone_cxx-i386.a OLD_FILES+=usr/lib/clang/3.9.1/lib/freebsd/libclang_rt.ubsan_standalone_cxx-i386.a

View File

@ -1635,7 +1635,7 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
bool device_tree::parse_define(const char *def) bool device_tree::parse_define(const char *def)
{ {
char *val = strchr(def, '='); const char *val = strchr(def, '=');
if (!val) if (!val)
{ {
if (strlen(def) != 0) if (strlen(def) != 0)

View File

@ -68,22 +68,22 @@
* leaf nodes count. * leaf nodes count.
*/ */
typedef struct { typedef struct {
int symbol_size; /* Size of the symbol table */ int symbol_size; /* Size of the symbol table */
int treelevels; /* Levels for the huffman tree */ int treelevels; /* Levels for the huffman tree */
int *symbolsin; /* Table of leaf symbols count in int *symbolsin; /* Table of leaf symbols count in each
each level */ * level */
int *inodesin; /* Table of internal nodes count in int *inodesin; /* Table of internal nodes count in
each level */ * each level */
char *symbol; /* The symbol table */ char *symbol; /* The symbol table */
char *symbol_eob; /* Pointer to the EOB symbol */ char *symbol_eob; /* Pointer to the EOB symbol */
char **tree; /* Decoding huffman tree (pointers to char **tree; /* Decoding huffman tree (pointers to
first symbol of each tree level */ * first symbol of each tree level */
off_t uncompressed_size; /* Uncompressed size */ off_t uncompressed_size; /* Uncompressed size */
FILE *fpIn; /* Input stream */ FILE *fpIn; /* Input stream */
FILE *fpOut; /* Output stream */ FILE *fpOut; /* Output stream */
} unpack_descriptor_t; } unpack_descriptor_t;
/* /*
@ -122,7 +122,7 @@ unpackd_fill_inodesin(const unpack_descriptor_t *unpackd, int level)
if (level < unpackd->treelevels) { if (level < unpackd->treelevels) {
unpackd_fill_inodesin(unpackd, level + 1); unpackd_fill_inodesin(unpackd, level + 1);
unpackd->inodesin[level] = (unpackd->inodesin[level + 1] + unpackd->inodesin[level] = (unpackd->inodesin[level + 1] +
unpackd->symbolsin[level + 1]) / 2; unpackd->symbolsin[level + 1]) / 2;
} else } else
unpackd->inodesin[level] = 0; unpackd->inodesin[level] = 0;
} }
@ -163,7 +163,7 @@ unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in,
accepted_bytes(bytes_in, PACK_HEADER_LENGTH); accepted_bytes(bytes_in, PACK_HEADER_LENGTH);
/* Obtain uncompressed length (bytes 2,3,4,5)*/ /* Obtain uncompressed length (bytes 2,3,4,5) */
unpackd->uncompressed_size = 0; unpackd->uncompressed_size = 0;
for (i = 2; i <= 5; i++) { for (i = 2; i <= 5; i++) {
unpackd->uncompressed_size <<= 8; unpackd->uncompressed_size <<= 8;
@ -187,7 +187,7 @@ unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in,
unpackd->symbolsin = unpackd->symbolsin =
calloc(unpackd->treelevels, sizeof(*(unpackd->symbolsin))); calloc(unpackd->treelevels, sizeof(*(unpackd->symbolsin)));
unpackd->tree = unpackd->tree =
calloc(unpackd->treelevels, (sizeof (*(unpackd->tree)))); calloc(unpackd->treelevels, (sizeof(*(unpackd->tree))));
if (unpackd->inodesin == NULL || unpackd->symbolsin == NULL || if (unpackd->inodesin == NULL || unpackd->symbolsin == NULL ||
unpackd->tree == NULL) unpackd->tree == NULL)
maybe_err("calloc"); maybe_err("calloc");
@ -196,7 +196,7 @@ unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in,
unpackd->treelevels--; unpackd->treelevels--;
/* Read the levels symbol count table and calculate total */ /* Read the levels symbol count table and calculate total */
unpackd->symbol_size = 1; /* EOB */ unpackd->symbol_size = 1; /* EOB */
for (i = 0; i <= unpackd->treelevels; i++) { for (i = 0; i <= unpackd->treelevels; i++) {
if ((thisbyte = fgetc(unpackd->fpIn)) == EOF) if ((thisbyte = fgetc(unpackd->fpIn)) == EOF)
maybe_err("File appears to be truncated"); maybe_err("File appears to be truncated");
@ -310,7 +310,7 @@ unpack_decode(const unpack_descriptor_t *unpackd, off_t *bytes_in)
static off_t static off_t
unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in) unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in)
{ {
unpack_descriptor_t unpackd; unpack_descriptor_t unpackd;
in = dup(in); in = dup(in);
if (in == -1) if (in == -1)
@ -326,4 +326,3 @@ unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in)
/* If we reached here, the unpack was successful */ /* If we reached here, the unpack was successful */
return (unpackd.uncompressed_size); return (unpackd.uncompressed_size);
} }

View File

@ -28,7 +28,7 @@
.\" From: @(#)nfsstat.1 8.1 (Berkeley) 6/6/93 .\" From: @(#)nfsstat.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd August 13, 2016 .Dd November 23, 2016
.Dt NFSSTAT 1 .Dt NFSSTAT 1
.Os .Os
.Sh NAME .Sh NAME
@ -38,7 +38,7 @@
statistics statistics
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl cdemszW .Op Fl cdEemszW
.Op Fl M Ar core .Op Fl M Ar core
.Op Fl N Ar system .Op Fl N Ar system
.Op Fl w Ar wait .Op Fl w Ar wait
@ -82,6 +82,16 @@ will increase by more than the measurement interval.
.It Fl e .It Fl e
Report the extra statistics collected by the NFS client and Report the extra statistics collected by the NFS client and
server for NFSv4. server for NFSv4.
.It Fl E
Similar to
.Fl e
except that the statistics include NFSv4.1 and the numbers aren't clipped
at one billion.
Only one of
.Fl e
or
.Fl E
can be specified.
.It Fl M .It Fl M
Extract values associated with the name list from the specified core Extract values associated with the name list from the specified core
instead of the default instead of the default

View File

@ -113,6 +113,7 @@ static void usage(void);
static char *sperc1(int, int); static char *sperc1(int, int);
static char *sperc2(int, int); static char *sperc2(int, int);
static void exp_intpr(int, int); static void exp_intpr(int, int);
static void exp41_intpr(int, int);
static void exp_sidewaysintpr(u_int, int, int, int); static void exp_sidewaysintpr(u_int, int, int, int);
static void compute_new_stats(struct nfsstatsv1 *cur_stats, static void compute_new_stats(struct nfsstatsv1 *cur_stats,
struct nfsstatsv1 *prev_stats, int curop, long double etime, struct nfsstatsv1 *prev_stats, int curop, long double etime,
@ -155,7 +156,7 @@ main(int argc, char **argv)
interval = 0; interval = 0;
memf = nlistf = NULL; memf = nlistf = NULL;
while ((ch = getopt(argc, argv, "cdesWM:mN:w:z")) != -1) while ((ch = getopt(argc, argv, "cdEesWM:mN:w:z")) != -1)
switch(ch) { switch(ch) {
case 'M': case 'M':
memf = optarg; memf = optarg;
@ -208,7 +209,14 @@ main(int argc, char **argv)
case 'z': case 'z':
zflag = 1; zflag = 1;
break; break;
case 'E':
if (extra_output != 0)
errx(1, "-e and -E are mutually exclusive");
extra_output = 2;
break;
case 'e': case 'e':
if (extra_output != 0)
errx(1, "-e and -E are mutually exclusive");
extra_output = 1; extra_output = 1;
break; break;
case '?': case '?':
@ -236,7 +244,9 @@ main(int argc, char **argv)
exp_sidewaysintpr(interval, clientOnly, serverOnly, exp_sidewaysintpr(interval, clientOnly, serverOnly,
newStats); newStats);
} else { } else {
if (extra_output != 0) if (extra_output == 2)
exp41_intpr(clientOnly, serverOnly);
else if (extra_output == 1)
exp_intpr(clientOnly, serverOnly); exp_intpr(clientOnly, serverOnly);
else else
intpr(clientOnly, serverOnly); intpr(clientOnly, serverOnly);
@ -795,6 +805,362 @@ exp_intpr(int clientOnly, int serverOnly)
} }
} }
/*
* Print a description of the nfs stats for the client/server,
* including NFSv4.1.
*/
static void
exp41_intpr(int clientOnly, int serverOnly)
{
int nfssvc_flag;
nfssvc_flag = NFSSVC_GETSTATS | NFSSVC_NEWSTRUCT;
if (zflag != 0) {
if (clientOnly != 0)
nfssvc_flag |= NFSSVC_ZEROCLTSTATS;
if (serverOnly != 0)
nfssvc_flag |= NFSSVC_ZEROSRVSTATS;
}
ext_nfsstats.vers = NFSSTATS_V1;
if (nfssvc(nfssvc_flag, &ext_nfsstats) < 0)
err(1, "Can't get stats");
if (clientOnly != 0) {
if (printtitle) {
printf("Client Info:\n");
printf("RPC Counts:\n");
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Getattr", "Setattr", "Lookup", "Readlink", "Read",
"Write");
}
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETATTR],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETATTR],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOOKUP],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READLINK],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READ],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_WRITE]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Create", "Remove", "Rename", "Link", "Symlink",
"Mkdir");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATE],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_REMOVE],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RENAME],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LINK],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SYMLINK],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKDIR]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Rmdir", "Readdir", "RdirPlus", "Access", "Mknod",
"Fsstat");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RMDIR],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIR],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDIRPLUS],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_ACCESS],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_MKNOD],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSSTAT]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Fsinfo", "PathConf", "Commit", "SetClId",
"SetClIdCf", "Lock");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FSINFO],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_PATHCONF],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COMMIT],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETCLIENTID],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETCLIENTIDCFRM],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCK]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"LockT", "LockU", "Open", "OpenCfr", "OpenDownGr",
"Close");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCKT],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LOCKU],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPEN],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPENCONFIRM],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_OPENDOWNGRADE],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CLOSE]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"RelLckOwn", "FreeStateID", "PutRootFH", "DelegRet",
"GetACL", "SetACL");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RELEASELCKOWN],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_FREESTATEID],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_PUTROOTFH],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DELEGRETURN],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETACL],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_SETACL]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"ExchangeID", "CreateSess", "DestroySess",
"DestroyClId", "LayoutGet", "GetDevInfo");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_EXCHANGEID],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_CREATESESSION],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DESTROYSESSION],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_DESTROYCLIENT],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTGET],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_GETDEVICEINFO]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"LayoutCommit", "LayoutReturn", "ReclaimCompl",
"ReadDataS", "WriteDataS", "CommitDataS");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTCOMMIT],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_LAYOUTRETURN],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_RECLAIMCOMPL],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_READDS],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_WRITEDS],
(uintmax_t)ext_nfsstats.rpccnt[NFSPROC_COMMITDS]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"OpenOwner", "Opens", "LockOwner", "Locks",
"Delegs", "LocalOwn");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.clopenowners,
(uintmax_t)ext_nfsstats.clopens,
(uintmax_t)ext_nfsstats.cllockowners,
(uintmax_t)ext_nfsstats.cllocks,
(uintmax_t)ext_nfsstats.cldelegates,
(uintmax_t)ext_nfsstats.cllocalopenowners);
if (printtitle)
printf("%12.12s %12.12s %12.12s\n",
"LocalOpen", "LocalLOwn", "LocalLock");
printf("%12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.cllocalopens,
(uintmax_t)ext_nfsstats.cllocallockowners,
(uintmax_t)ext_nfsstats.cllocallocks);
if (printtitle) {
printf("Rpc Info:\n");
printf("%12.12s %12.12s %12.12s %12.12s %12.12s\n",
"TimedOut", "Invalid", "X Replies", "Retries",
"Requests");
}
printf("%12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.rpctimeouts,
(uintmax_t)ext_nfsstats.rpcinvalid,
(uintmax_t)ext_nfsstats.rpcunexpected,
(uintmax_t)ext_nfsstats.rpcretries,
(uintmax_t)ext_nfsstats.rpcrequests);
if (printtitle) {
printf("Cache Info:\n");
printf("%12.12s %12.12s %12.12s %12.12s\n",
"Attr Hits", "Misses", "Lkup Hits", "Misses");
}
printf("%12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.attrcache_hits,
(uintmax_t)ext_nfsstats.attrcache_misses,
(uintmax_t)ext_nfsstats.lookupcache_hits,
(uintmax_t)ext_nfsstats.lookupcache_misses);
if (printtitle)
printf("%12.12s %12.12s %12.12s %12.12s\n",
"BioR Hits", "Misses", "BioW Hits", "Misses");
printf("%12ju %12ju %12ju %12ju\n",
(uintmax_t)(ext_nfsstats.biocache_reads -
ext_nfsstats.read_bios),
(uintmax_t)ext_nfsstats.read_bios,
(uintmax_t)(ext_nfsstats.biocache_writes -
ext_nfsstats.write_bios),
(uintmax_t)ext_nfsstats.write_bios);
if (printtitle)
printf("%12.12s %12.12s %12.12s %12.12s\n",
"BioRLHits", "Misses", "BioD Hits", "Misses");
printf("%12ju %12ju %12ju %12ju\n",
(uintmax_t)(ext_nfsstats.biocache_readlinks -
ext_nfsstats.readlink_bios),
(uintmax_t)ext_nfsstats.readlink_bios,
(uintmax_t)(ext_nfsstats.biocache_readdirs -
ext_nfsstats.readdir_bios),
(uintmax_t)ext_nfsstats.readdir_bios);
if (printtitle)
printf("%12.12s %12.12s\n", "DirE Hits", "Misses");
printf("%12ju %12ju\n",
(uintmax_t)ext_nfsstats.direofcache_hits,
(uintmax_t)ext_nfsstats.direofcache_misses);
}
if (serverOnly != 0) {
if (printtitle) {
printf("\nServer Info:\n");
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Getattr", "Setattr", "Lookup", "Readlink",
"Read", "Write");
}
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETATTR],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETATTR],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOOKUP],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READLINK],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READ],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WRITE]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Create", "Remove", "Rename", "Link", "Symlink",
"Mkdir");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_V3CREATE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_REMOVE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RENAME],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LINK],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SYMLINK],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKDIR]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Rmdir", "Readdir", "RdirPlus", "Access", "Mknod",
"Fsstat");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RMDIR],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIR],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_READDIRPLUS],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_ACCESS],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_MKNOD],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSSTAT]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Fsinfo", "PathConf", "Commit", "LookupP",
"SetClId", "SetClIdCf");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FSINFO],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PATHCONF],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_COMMIT],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOOKUPP],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETCLIENTID],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETCLIENTIDCFRM]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Open", "OpenAttr", "OpenDwnGr", "OpenCfrm",
"DelePurge", "DeleRet");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPEN],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENATTR],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENDOWNGRADE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_OPENCONFIRM],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DELEGPURGE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DELEGRETURN]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"GetFH", "Lock", "LockT", "LockU", "Close",
"Verify");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETFH],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCK],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCKT],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LOCKU],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CLOSE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_VERIFY]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"NVerify", "PutFH", "PutPubFH", "PutRootFH",
"Renew", "RestoreFH");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_NVERIFY],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTFH],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTPUBFH],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_PUTROOTFH],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RENEW],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RESTOREFH]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"SaveFH", "Secinfo", "RelLckOwn", "V4Create",
"BackChannelCtrl", "BindConnToSess");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SAVEFH],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SECINFO],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RELEASELCKOWN],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CREATE],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_BACKCHANNELCTL],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_BINDCONNTOSESS]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"ExchangeID", "CreateSess", "DestroySess",
"FreeStateID", "GetDirDeleg", "GetDevInfo");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_EXCHANGEID],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_CREATESESSION],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DESTROYSESSION],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_FREESTATEID],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDIRDELEG],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDEVINFO]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"GetDevList", "LayoutCommit", "LayoutGet",
"LayoutReturn", "SecInfNoName", "Sequence");
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_GETDEVLIST],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTCOMMIT],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTGET],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_LAYOUTRETURN],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SECINFONONAME],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SEQUENCE]);
if (printtitle)
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s\n",
"SetSSV", "TestStateID", "WantDeleg",
"DestroyClID", "ReclaimCompl");
printf("%12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_SETSSV],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_TESTSTATEID],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_WANTDELEG],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_DESTROYCLIENTID],
(uintmax_t)ext_nfsstats.srvrpccnt[NFSV4OP_RECLAIMCOMPL]);
if (printtitle) {
printf("Server:\n");
printf("%12.12s %12.12s %12.12s\n",
"Retfailed", "Faults", "Clients");
}
printf("%12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srv_errs,
(uintmax_t)ext_nfsstats.srvrpc_errs,
(uintmax_t)ext_nfsstats.srvclients);
if (printtitle)
printf("%12.12s %12.12s %12.12s %12.12s %12.12s\n",
"OpenOwner", "Opens", "LockOwner",
"Locks", "Delegs");
printf("%12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvopenowners,
(uintmax_t)ext_nfsstats.srvopens,
(uintmax_t)ext_nfsstats.srvlockowners,
(uintmax_t)ext_nfsstats.srvlocks,
(uintmax_t)ext_nfsstats.srvdelegates);
if (printtitle) {
printf("Server Cache Stats:\n");
printf(
"%12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n",
"Inprog", "Idem", "Non-idem", "Misses",
"CacheSize", "TCPPeak");
}
printf("%12ju %12ju %12ju %12ju %12ju %12ju\n",
(uintmax_t)ext_nfsstats.srvcache_inproghits,
(uintmax_t)ext_nfsstats.srvcache_idemdonehits,
(uintmax_t)ext_nfsstats.srvcache_nonidemdonehits,
(uintmax_t)ext_nfsstats.srvcache_misses,
(uintmax_t)ext_nfsstats.srvcache_size,
(uintmax_t)ext_nfsstats.srvcache_tcppeak);
}
}
static void static void
compute_totals(struct nfsstatsv1 *total_stats, struct nfsstatsv1 *cur_stats) compute_totals(struct nfsstatsv1 *total_stats, struct nfsstatsv1 *cur_stats)
{ {

View File

@ -1070,7 +1070,7 @@ cd9660_rename_filename(cd9660node *iter, int num, int delete_chars)
tmp = malloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING); tmp = malloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING);
while (i < num) { while (i < num && iter) {
powers = 1; powers = 1;
count = 0; count = 0;
digits = 1; digits = 1;

View File

@ -1835,6 +1835,7 @@ init(int signo)
free((char *)f); free((char *)f);
} }
Files = NULL; Files = NULL;
*nextp = NULL;
/* open the configuration file */ /* open the configuration file */
if ((cf = fopen(ConfFile, "r")) == NULL) { if ((cf = fopen(ConfFile, "r")) == NULL) {