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; > }