On Sun, Nov 03, 2019 at 10:06:18AM +0100, Patrick Wildt wrote:
> Obiously I missed the attachment.
Could this tool be put into base or ports / pkg_add?
> /*
> * Copyright (c) 2013 Broadcom Corporation
> *
> * Permission to use, copy, modify, and/or distribute this software for any
> * purpose with or without fee is hereby granted, provided that the above
> * copyright notice and this permission notice appear in all copies.
> *
> * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
> * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> ACTION
> * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> */
>
> #include <sys/param.h>
> #include <sys/stat.h>
> #include <stdlib.h>
> #include <stdio.h>
> #include <string.h>
> #include <unistd.h>
> #include <errno.h>
> #include <fcntl.h>
> //#include <linux/bcm47xx_nvram.h>
>
> #define BRCMF_FW_MAX_NVRAM_SIZE 64000
> #define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
> #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
> #define BRCMF_FW_DEFAULT_BOARDREV "boardrev=0xff"
>
> enum nvram_parser_state {
> IDLE,
> KEY,
> VALUE,
> COMMENT,
> END
> };
>
> /**
> * struct nvram_parser - internal info for parser.
> *
> * @state: current parser state.
> * @data: input buffer being parsed.
> * @nvram: output buffer with parse result.
> * @nvram_len: lenght of parse result.
> * @line: current line.
> * @column: current column in line.
> * @pos: byte offset in input buffer.
> * @entry: start position of key,value entry.
> * @multi_dev_v1: detect pcie multi device v1 (compressed).
> * @multi_dev_v2: detect pcie multi device v2.
> * @boardrev_found: nvram contains boardrev information.
> */
> struct nvram_parser {
> enum nvram_parser_state state;
> char *data;
> char *nvram;
> uint32_t nvram_len;
> uint32_t line;
> uint32_t column;
> uint32_t pos;
> uint32_t entry;
> int multi_dev_v1;
> int multi_dev_v2;
> int boardrev_found;
> };
>
> /**
> * is_nvram_char() - check if char is a valid one for NVRAM entry
> *
> * It accepts all printable ASCII chars except for '#' which opens a comment.
> * Please note that ' ' (space) while accepted is not a valid key name char.
> */
> static int is_nvram_char(char c)
> {
> /* comment marker excluded */
> if (c == '#')
> return 0;
>
> /* key and value may have any other readable character */
> return (c >= 0x20 && c < 0x7f);
> }
>
> static int is_whitespace(char c)
> {
> return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
> }
>
> static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser
> *nvp)
> {
> char c;
>
> c = nvp->data[nvp->pos];
> if (c == '\n')
> return COMMENT;
> if (is_whitespace(c) || c == '\0')
> goto proceed;
> if (c == '#')
> return COMMENT;
> if (is_nvram_char(c)) {
> nvp->entry = nvp->pos;
> return KEY;
> }
> printf("warning: ln=%d:col=%d: ignoring invalid character\n",
> nvp->line, nvp->column);
> proceed:
> nvp->column++;
> nvp->pos++;
> return IDLE;
> }
>
> static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser
> *nvp)
> {
> enum nvram_parser_state st = nvp->state;
> char c;
>
> c = nvp->data[nvp->pos];
> if (c == '=') {
> /* ignore RAW1 by treating as comment */
> if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
> st = COMMENT;
> else
> st = VALUE;
> if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
> nvp->multi_dev_v1 = 1;
> if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
> nvp->multi_dev_v2 = 1;
> if (strncmp(&nvp->data[nvp->entry], "boardrev", 8) == 0)
> nvp->boardrev_found = 1;
> } else if (!is_nvram_char(c) || c == ' ') {
> printf("warning: ln=%d:col=%d: '=' expected, skip invalid key
> entry\n",
> nvp->line, nvp->column);
> return COMMENT;
> }
>
> nvp->column++;
> nvp->pos++;
> return st;
> }
>
> static enum nvram_parser_state
> brcmf_nvram_handle_value(struct nvram_parser *nvp)
> {
> char c;
> char *skv;
> char *ekv;
> uint32_t cplen;
>
> c = nvp->data[nvp->pos];
> if (!is_nvram_char(c)) {
> /* key,value pair complete */
> ekv = &nvp->data[nvp->pos];
> skv = &nvp->data[nvp->entry];
> cplen = ekv - skv;
> if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
> return END;
> /* copy to output buffer */
> memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
> nvp->nvram_len += cplen;
> nvp->nvram[nvp->nvram_len] = '\0';
> nvp->nvram_len++;
> return IDLE;
> }
> nvp->pos++;
> nvp->column++;
> return VALUE;
> }
>
> static enum nvram_parser_state
> brcmf_nvram_handle_comment(struct nvram_parser *nvp)
> {
> char *eoc, *sol;
>
> sol = (char *)&nvp->data[nvp->pos];
> eoc = strchr(sol, '\n');
> if (!eoc) {
> eoc = strchr(sol, '\0');
> if (!eoc)
> return END;
> }
>
> /* eat all moving to next line */
> nvp->line++;
> nvp->column = 1;
> nvp->pos += (eoc - sol) + 1;
> return IDLE;
> }
>
> static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser
> *nvp)
> {
> /* final state */
> return END;
> }
>
> static enum nvram_parser_state
> (*nv_parser_states[])(struct nvram_parser *nvp) = {
> brcmf_nvram_handle_idle,
> brcmf_nvram_handle_key,
> brcmf_nvram_handle_value,
> brcmf_nvram_handle_comment,
> brcmf_nvram_handle_end
> };
>
> static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
> char *data, size_t data_len)
> {
> size_t size;
>
> memset(nvp, 0, sizeof(*nvp));
> nvp->data = data;
> /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
> if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
> size = BRCMF_FW_MAX_NVRAM_SIZE;
> else
> size = data_len;
> /* Alloc for extra 0 byte + roundup by 4 + length field */
> size += 1 + 3 + sizeof(uint32_t);
> nvp->nvram = malloc(size);
> if (!nvp->nvram)
> return ENOMEM;
>
> nvp->line = 1;
> nvp->column = 1;
> return 0;
> }
>
> /* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
> * devices. Strip it down for one device, use domain_nr/bus_nr to determine
> * which data is to be returned. v1 is the version where nvram is stored
> * compressed and "devpath" maps to index for valid entries.
> */
> static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, uint16_t
> domain_nr,
> uint16_t bus_nr)
> {
> /* Device path with a leading '=' key-value separator */
> char pci_path[] = "=pci/?/?";
> size_t pci_len;
> char pcie_path[] = "=pcie/?/?";
> size_t pcie_len;
>
> uint32_t i, j;
> int found;
> char *nvram;
> uint8_t id;
>
> nvram = malloc(nvp->nvram_len + 1 + 3 + sizeof(uint32_t));
> if (!nvram)
> goto fail;
>
> /* min length: devpath0=pcie/1/4/ + 0:x=y */
> if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
> goto fail;
>
> /* First search for the devpathX and see if it is the configuration
> * for domain_nr/bus_nr. Search complete nvp
> */
> snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
> bus_nr);
> pci_len = strlen(pci_path);
> snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
> bus_nr);
> pcie_len = strlen(pcie_path);
> found = 0;
> i = 0;
> while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
> /* Format: devpathX=pcie/Y/Z/
> * Y = domain_nr, Z = bus_nr, X = virtual ID
> */
> if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
> (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
> !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
> id = nvp->nvram[i + 7] - '0';
> found = 1;
> break;
> }
> while (nvp->nvram[i] != 0)
> i++;
> i++;
> }
> if (!found)
> goto fail;
>
> /* Now copy all valid entries, release old nvram and assign new one */
> i = 0;
> j = 0;
> while (i < nvp->nvram_len) {
> if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
> i += 2;
> if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
> nvp->boardrev_found = 1;
> while (nvp->nvram[i] != 0) {
> nvram[j] = nvp->nvram[i];
> i++;
> j++;
> }
> nvram[j] = 0;
> j++;
> }
> while (nvp->nvram[i] != 0)
> i++;
> i++;
> }
> free(nvp->nvram);
> nvp->nvram = nvram;
> nvp->nvram_len = j;
> return;
>
> fail:
> free(nvram);
> nvp->nvram_len = 0;
> }
>
> /* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
> * devices. Strip it down for one device, use domain_nr/bus_nr to determine
> * which data is to be returned. v2 is the version where nvram is stored
> * uncompressed, all relevant valid entries are identified by
> * pcie/domain_nr/bus_nr:
> */
> static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, uint16_t
> domain_nr,
> uint16_t bus_nr)
> {
> char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
> size_t len;
> uint32_t i, j;
> char *nvram;
>
> nvram = malloc(nvp->nvram_len + 1 + 3 + sizeof(uint32_t));
> if (!nvram)
> goto fail;
>
> /* Copy all valid entries, release old nvram and assign new one.
> * Valid entries are of type pcie/X/Y/ where X = domain_nr and
> * Y = bus_nr.
> */
> snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
> len = strlen(prefix);
> i = 0;
> j = 0;
> while (i < nvp->nvram_len - len) {
> if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
> i += len;
> if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
> nvp->boardrev_found = 1;
> while (nvp->nvram[i] != 0) {
> nvram[j] = nvp->nvram[i];
> i++;
> j++;
> }
> nvram[j] = 0;
> j++;
> }
> while (nvp->nvram[i] != 0)
> i++;
> i++;
> }
> free(nvp->nvram);
> nvp->nvram = nvram;
> nvp->nvram_len = j;
> return;
> fail:
> free(nvram);
> nvp->nvram_len = 0;
> }
>
> static void brcmf_fw_add_defaults(struct nvram_parser *nvp)
> {
> if (nvp->boardrev_found)
> return;
>
> memcpy(&nvp->nvram[nvp->nvram_len], &BRCMF_FW_DEFAULT_BOARDREV,
> strlen(BRCMF_FW_DEFAULT_BOARDREV));
> nvp->nvram_len += strlen(BRCMF_FW_DEFAULT_BOARDREV);
> nvp->nvram[nvp->nvram_len] = '\0';
> nvp->nvram_len++;
> }
>
> /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a
> fil
> * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
> * and converts newlines to NULs. Shortens buffer as needed and pads with
> NULs.
> * End of buffer is completed with token identifying length of buffer.
> */
> static void *brcmf_fw_nvram_strip(char *data, size_t data_len,
> uint32_t *new_length, uint16_t domain_nr,
> uint16_t bus_nr)
> {
> struct nvram_parser nvp;
> uint32_t pad;
> uint32_t token;
> uint32_t token_le;
>
> if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
> return NULL;
>
> while (nvp.pos < data_len) {
> nvp.state = nv_parser_states[nvp.state](&nvp);
> if (nvp.state == END)
> break;
> }
> if (nvp.multi_dev_v1) {
> nvp.boardrev_found = 0;
> brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
> } else if (nvp.multi_dev_v2) {
> nvp.boardrev_found = 0;
> brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
> }
>
> if (nvp.nvram_len == 0) {
> free(nvp.nvram);
> return NULL;
> }
>
> brcmf_fw_add_defaults(&nvp);
>
> pad = nvp.nvram_len;
> *new_length = roundup(nvp.nvram_len + 1, 4);
> while (pad != *new_length) {
> nvp.nvram[pad] = 0;
> pad++;
> }
>
> token = *new_length / 4;
> token = (~token << 16) | (token & 0x0000FFFF);
> token_le = htole32(token);
>
> memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
> *new_length += sizeof(token_le);
>
> return nvp.nvram;
> }
>
> int
> main(int argc, char **argv)
> {
> int fd;
> struct stat sb;
> char *buf, *nvram;
> ssize_t buflen;
> uint32_t nvram_length = 0;
>
> fd = open(argv[1], O_RDONLY);
> if (fd == -1)
> return 1;
>
> if (fstat(fd, &sb) == -1)
> return 1;
>
> buflen = sb.st_size;
> if ((buf = malloc(buflen)) == NULL)
> return 1;
>
> if (read(fd, buf, buflen) != buflen) {
> free(buf);
> return 1;
> }
>
> nvram = brcmf_fw_nvram_strip(buf, buflen, &nvram_length, 0, 0);
> if (nvram == NULL) {
> free(buf);
> return 1;
> }
>
> close(fd);
> fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644);
> if (fd == -1) {
> free(buf);
> free(nvram);
> return 1;
> }
>
> if (write(fd, nvram, nvram_length) != (ssize_t)nvram_length) {
> free(buf);
> free(nvram);
> return 1;
> }
>
> close(fd);
> free(buf);
> free(nvram);
> return 0;
> }