Hi Patrick, Thanks a lot for your answer. You told me exactly what I was looking for. I've generated a 32bit EFI grub image and placed it inside an archiso bootable image, booted from Arch Linux, grabbed the EFI var, came back to my OpenBSD installation, compiled the program you attached and after generating the nvram file everything works like a charm.
I think I'll write a post on my blog to have a quick reference for the next times. Many thanks for your time. Best Regards Stefano Sent: Sunday, November 03, 2019 at 2:06 PM From: "Stefan Sperling" <s...@stsp.name> To: "Patrick Wildt" <patr...@blueri.se> Cc: "Stefano Enrico Mendola" <hy...@gmx.com>, "Brad Smith" <b...@comstyle.com>, misc@openbsd.org Subject: Re: Broadcom firmwares and nvram filesOn 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; > }