#include <sys/cdefs.h>
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/ctype.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#else
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_datavar.h"
#include "bhnd_nvram_data_bcmvar.h"
struct bhnd_nvram_bcmraw;
struct bhnd_nvram_bcmraw {
struct bhnd_nvram_data nv;
char *data;
size_t size;
size_t count;
};
BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))
static int
bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
{
char envp[16];
size_t envp_len;
size_t io_size;
int error;
io_size = bhnd_nvram_io_getsize(io);
envp_len = bhnd_nv_ummin(sizeof(envp), io_size);
if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
return (error);
if (envp_len == 0)
return (ENXIO);
if (envp_len == 1) {
if (envp[0] != '\0')
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_MAYBE);
}
for (size_t i = 0; i < envp_len; i++) {
char c = envp[i];
if (c == '\n')
return (ENXIO);
if (c == '\0' && !bhnd_nv_isprint(c))
continue;
}
envp_len = 2;
if (io_size < envp_len)
return (ENXIO);
if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))
return (error);
if (envp[0] != '\0' || envp[1] != '\0')
return (ENXIO);
return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
}
static int
bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name,
void *buf, size_t *len, bhnd_nvram_type type)
{
return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type,
false));
}
static int
bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
bhnd_nvram_plist *options, void *outp, size_t *olen)
{
bhnd_nvram_prop *prop;
size_t limit, nbytes;
int error;
if (outp != NULL)
limit = *olen;
else
limit = 0;
nbytes = 0;
prop = NULL;
while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
const char *name;
char *p;
size_t prop_limit;
size_t name_len, value_len;
if (outp == NULL || limit < nbytes) {
p = NULL;
prop_limit = 0;
} else {
p = ((char *)outp) + nbytes;
prop_limit = limit - nbytes;
}
name = bhnd_nvram_prop_name(prop);
name_len = strlen(name) + 1;
if (prop_limit > name_len) {
memcpy(p, name, name_len - 1);
p[name_len - 1] = '=';
prop_limit -= name_len;
p += name_len;
} else {
prop_limit = 0;
p = NULL;
}
if (SIZE_MAX - nbytes < name_len)
return (EFTYPE);
nbytes += name_len;
value_len = prop_limit;
error = bhnd_nvram_prop_encode(prop, p, &value_len,
BHND_NVRAM_TYPE_STRING);
if (error && error != ENOMEM) {
BHND_NV_LOG("error serializing %s to required type "
"%s: %d\n", name,
bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
error);
return (error);
}
if (SIZE_MAX - nbytes < value_len)
return (EFTYPE);
nbytes += value_len;
}
if (limit > nbytes)
*((char *)outp + nbytes) = '\0';
if (nbytes == SIZE_MAX)
return (EFTYPE);
else
nbytes++;
*olen = nbytes;
if (limit < *olen) {
if (outp == NULL)
return (0);
return (ENOMEM);
}
return (0);
}
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;
io_size = bhnd_nvram_io_getsize(src);
if (io_size == SIZE_MAX)
return (ENOMEM);
capacity = io_size + 1 ;
bcm->size = io_size;
if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
return (ENOMEM);
if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
return (error);
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;
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);
}
*(envp + name_len) = '\0';
bcm->count++;
offset += envp_len;
if (offset == io_size) {
BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
offset);
return (EINVAL);
}
if (++offset == bcm->size) {
BHND_NV_ASSERT(offset < capacity,
("appending past end of buffer"));
bcm->size++;
*(bcm->data + offset) = '\0';
}
if (*(bcm->data + offset) == '\0')
break;
}
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;
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 bhnd_nvram_plist *
bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
{
return (NULL);
}
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 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) {
envp = bcm->data;
} else {
envp = *cookiep;
envp += strlen(envp) + 1;
envp += strlen(envp) + 1;
}
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_order(struct bhnd_nvram_data *nv, void *cookiep1,
void *cookiep2)
{
if (cookiep1 < cookiep2)
return (-1);
if (cookiep1 > cookiep2)
return (1);
return (0);
}
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 int
bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
bhnd_nvram_val **value)
{
return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
}
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;
envp = cookiep;
envp += strlen(envp) + 1;
*len = strlen(envp) + 1;
*type = BHND_NVRAM_TYPE_STRING;
return (envp);
}
static const char *
bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
{
return (cookiep);
}
static int
bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
bhnd_nvram_val *value, bhnd_nvram_val **result)
{
bhnd_nvram_val *str;
int error;
if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
return (EINVAL);
error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
value, BHND_NVRAM_VAL_DYNAMIC);
if (error)
return (error);
*result = str;
return (0);
}
static int
bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
{
return (0);
}