On Sun, Nov 03, 2019 at 10:05:38AM +0100, Patrick Wildt wrote:
> On Sun, Nov 03, 2019 at 12:08:08AM +0100, Stefano Enrico Mendola wrote:
> > Hi,
> > 
> > my bad, I thought the grepped output was enough.
> > Here's the complete dmesg(8) output. =============================
> > OpenBSD 6.6 (GENERIC.MP) #372: Sat Oct 12 10:56:27 MDT 2019
> > [email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
> > real mem = 2056568832 (1961MB)
> > avail mem = 1981595648 (1889MB)
> > mpath0 at root
> > scsibus0 at mpath0: 256 targets
> > mainbus0 at root
> > bios0 at mainbus0: SMBIOS rev. 2.7 @ 0x7c31a010 (16 entries)
> > bios0: vendor American Megatrends Inc. version "X205TA.212" date
> > 09/04/2015
> > bios0: ASUSTeK COMPUTER INC. X205TA
> > acpi0 at bios0: ACPI 5.0
> > acpi0: sleep states S0 S5
> > acpi0: tables DSDT FACP TCPA UEFI OEM0 DBG2 HPET LPIT APIC MCFG SSDT SSDT
> > SSDT SSDT FPDT SSDT SSDT SSDT SSDT TPM2 BGRT CSRT MSDM
> > acpi0: wakeup devices WLAN(S0)
> > acpihpet0 at acpi0: 14318179 Hz
> > acpimadt0 at acpi0 addr 0xfee00000
> > cpu0 at mainbus0: apid 0 (boot processor)
> > cpu0: Intel(R) Atom(TM) CPU Z3735F @ 1.33GHz, 1333.58 MHz, 06-37-08
> > cpu0:
> > FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,MOVBE,POPCNT,DEADLINE,AES,RDRAND,NXE,RDTSCP,LONG,LAHF,3DNOWP,PERF,ITSC,TSC_ADJUST,SMEP,ERMS,MD_CLEAR,IBRS,IBPB,STIBP,SENSOR,ARAT,MELTDOWN
> > cpu0: 1MB 64b/line 16-way L2 cache
> > cpu0: smt 0, core 0, package 0
> > mtrr: Pentium Pro MTRR support, 8 var ranges, 88 fixed ranges
> > cpu0: apic clock running at 83MHz
> > cpu0: mwait min=64, max=64, C-substates=0.2.0.0.0.0.3.3, IBE
> > cpu1 at mainbus0: apid 2 (application processor)
> > cpu1: Intel(R) Atom(TM) CPU Z3735F @ 1.33GHz, 1333.33 MHz, 06-37-08
> > cpu1:
> > FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,MOVBE,POPCNT,DEADLINE,AES,RDRAND,NXE,RDTSCP,LONG,LAHF,3DNOWP,PERF,ITSC,TSC_ADJUST,SMEP,ERMS,MD_CLEAR,IBRS,IBPB,STIBP,SENSOR,ARAT,MELTDOWN
> > cpu1: 1MB 64b/line 16-way L2 cache
> > cpu1: smt 0, core 1, package 0
> > cpu2 at mainbus0: apid 4 (application processor)
> > cpu2: Intel(R) Atom(TM) CPU Z3735F @ 1.33GHz, 1333.34 MHz, 06-37-08
> > cpu2:
> > FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,MOVBE,POPCNT,DEADLINE,AES,RDRAND,NXE,RDTSCP,LONG,LAHF,3DNOWP,PERF,ITSC,TSC_ADJUST,SMEP,ERMS,MD_CLEAR,IBRS,IBPB,STIBP,SENSOR,ARAT,MELTDOWN
> > cpu2: 1MB 64b/line 16-way L2 cache
> > cpu2: smt 0, core 2, package 0
> > cpu3 at mainbus0: apid 6 (application processor)
> > cpu3: Intel(R) Atom(TM) CPU Z3735F @ 1.33GHz, 1333.34 MHz, 06-37-08
> > cpu3:
> > FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,MOVBE,POPCNT,DEADLINE,AES,RDRAND,NXE,RDTSCP,LONG,LAHF,3DNOWP,PERF,ITSC,TSC_ADJUST,SMEP,ERMS,MD_CLEAR,IBRS,IBPB,STIBP,SENSOR,ARAT,MELTDOWN
> > cpu3: 1MB 64b/line 16-way L2 cache
> > cpu3: smt 0, core 3, package 0
> > ioapic0 at mainbus0: apid 8 pa 0xfec00000, version 20, 87 pins, remapped
> > acpimcfg0 at acpi0
> > acpimcfg0: addr 0xe0000000, bus 0-255
> > acpiprt0 at acpi0: bus 0 (PCI0)
> > acpiec0 at acpi0: not present
> > acpicpu0 at acpi0: C3(10@1000 mwait.1@0x64), C2(10@500 mwait.1@0x51),
> > C1(1000@1 mwait.1), PSS
> > acpicpu1 at acpi0: C3(10@1000 mwait.1@0x64), C2(10@500 mwait.1@0x51),
> > C1(1000@1 mwait.1), PSS
> > acpicpu2 at acpi0: C3(10@1000 mwait.1@0x64), C2(10@500 mwait.1@0x51),
> > C1(1000@1 mwait.1), PSS
> > acpicpu3 at acpi0: C3(10@1000 mwait.1@0x64), C2(10@500 mwait.1@0x51),
> > C1(1000@1 mwait.1), PSS
> > acpipwrres0 at acpi0: PLPE
> > acpipwrres1 at acpi0: USBC, resource for XHC1, EHC1, OTG1
> > acpipwrres2 at acpi0: CLK0, resource for CAM1
> > acpipwrres3 at acpi0: CLK1, resource for CAM0
> > acpipwrres4 at acpi0: P28X
> > acpipwrres5 at acpi0: P18X
> > acpipwrres6 at acpi0: P28P
> > acpipwrres7 at acpi0: P18P
> > acpipwrres8 at acpi0: P28T, resource for CAM0, CAM1
> > acpipwrres9 at acpi0: P18T, resource for CAM0, CAM1
> > acpipwrres10 at acpi0: P1XT
> > acpitz0 at acpi0: no critical temperature defined
> > "INT3396" at acpi0 not configured
> > bytgpio0 at acpi0: GPO2 uid 3 addr 0xfed0e000/0x1000 irq 50, 44 pins
> > bytgpio1 at acpi0: GPO0 uid 1 addr 0xfed0c000/0x1000 irq 49, 102 pins
> > dwiic0 at acpi0 I2C5 addr 0x90932000/0x1000 irq 36
> > iic0 at dwiic0
> > tipmic0 at iic0 addr 0x5e irq 67
> > bytgpio2 at acpi0: GPO1 uid 2 addr 0xfed0d000/0x1000 irq 48, 28 pins
> > "80860F0A" at acpi0 not configured
> > dwiic1 at acpi0 I2C1 addr 0x9091a000/0x1000 irq 32
> > iic1 at dwiic1
> > ihidev0 at iic1 addr 0x68 irq 71, vendor 0xb05 product 0x8585, PDEC3393
> > ihidev0: 9 report ids
> > ikbd0 at ihidev0 reportid 1: 8 variable keys, 6 key codes
> > wskbd0 at ikbd0 mux 1
> > hid at ihidev0 reportid 3 not configured
> > hid at ihidev0 reportid 4 not configured
> > hid at ihidev0 reportid 6 not configured
> > hid at ihidev0 reportid 9 not configured
> > "INT33BD" at acpi0 not configured
> > acpibat0 at acpi0: BATC model "SR Real Battery" serial 123456789 type
> > LION oem "Intel SR 1"
> > acpicmos0 at acpi0
> > acpipci0 at acpi0 PCI0: 0x00000004 0x00000011 0x00000001
> > "80860F28" at acpi0 not configured
> > "INT0002" at acpi0 not configured
> > sdhc0 at acpi0 SDHA addr 0x9094d000/0x1000 irq 44
> > sdhc0: SDHC 3.0, 200 MHz base clock
> > sdmmc0 at sdhc0: 8-bit, sd high-speed, mmc high-speed, dma
> > sdhc1 at acpi0 SDHB addr 0x90905000/0x1000 irq 46
> > sdhc1: SDHC 3.0, 100 MHz base clock
> > sdmmc1 at sdhc1: 4-bit, sd high-speed, mmc high-speed, dma
> > sdhc2 at acpi0 SDHC addr 0x9090b000/0x1000 irq 47, gpio
> > sdhc2: SDHC 3.0, 100 MHz base clock
> > sdmmc2 at sdhc2: 4-bit, sd high-speed, mmc high-speed, dma
> > "INTL9C60" at acpi0 not configured
> > "INTL9C60" at acpi0 not configured
> > "BCM2E65" at acpi0 not configured
> > "80860F0A" at acpi0 not configured
> > dwiic2 at acpi0 I2C2 addr 0x90920000/0x1000 irq 33
> > iic2 at dwiic2
> > "10EC5648" at iic2 addr 0x1a not configured
> > dwiic3 at acpi0 I2C3 addr 0x90926000/0x1000, failed initializing
> > dwiic4 at acpi0 I2C4 addr 0x9092c000/0x1000 irq 35
> > iic3 at dwiic4
> > ihidev1 at iic3 addr 0x15 irq 72, vendor 0x4f3 product 0x401, ELAN0100
> > ihidev1: 93 report ids
> > ims0 at ihidev1 reportid 1: 2 buttons, Z dir
> > wsmouse0 at ims0 mux 0
> > hid at ihidev1 reportid 11 not configured
> > hid at ihidev1 reportid 12 not configured
> > hid at ihidev1 reportid 13 not configured
> > hid at ihidev1 reportid 93 not configured
> > "INTCFD9" at acpi0 not configured
> > "ACPI000C" at acpi0 not configured
> > "PNP0C14" at acpi0 not configured
> > "ATK4002" at acpi0 not configured
> > acpiac0 at acpi0: AC unit offline
> > acpibtn0 at acpi0: LID_
> > acpibtn1 at acpi0: PWRB
> > acpibtn2 at acpi0: SLPB
> > "INT3401" at acpi0 not configured
> > "INT3400" at acpi0 not configured
> > "INT3407" at acpi0 not configured
> > "INT3403" at acpi0 not configured
> > "INT3403" at acpi0 not configured
> > "INT3403" at acpi0 not configured
> > "INT3403" at acpi0 not configured
> > "INT3406" at acpi0 not configured
> > "MSFT0101" at acpi0 not configured
> > acpivideo0 at acpi0: GFX0
> > acpivout0 at acpivideo0: DD1F
> > cpu0: using VERW MDS workaround
> > cpu0: Enhanced SpeedStep 1333 MHz: speeds: 1329, 1328, 1245, 1162, 1079,
> > 996, 913, 830, 747, 664, 581, 498 MHz
> > pci0 at mainbus0 bus 0
> > pchb0 at pci0 dev 0 function 0 "Intel Bay Trail Host" rev 0x0f
> > inteldrm0 at pci0 dev 2 function 0 "Intel Bay Trail Video" rev 0x0f
> > drm0 at inteldrm0
> > inteldrm0: msi
> > xhci0 at pci0 dev 20 function 0 "Intel Bay Trail xHCI" rev 0x0f: msi,
> > xHCI 1.0
> > usb0 at xhci0: USB revision 3.0
> > uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev
> > 3.00/1.00 addr 1
> > "Intel Bay Trail TXE" rev 0x0f at pci0 dev 26 function 0 not configured
> > pcib0 at pci0 dev 31 function 0 "Intel Bay Trail LPC" rev 0x0f
> > isa0 at pcib0
> > isadma0 at isa0
> > com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
> > pcppi0 at isa0 port 0x61
> > spkr0 at pcppi0
> > vmm0 at mainbus0: VMX/EPT (using slow L1TF mitigation)
> > efifb at mainbus0 not configured
> > bwfm0 at sdmmc1 function 1
> > manufacturer 0x02d0, product 0x4334 at sdmmc1 function 2 not configured
> > manufacturer 0x02d0, product 0x4334 at sdmmc1 function 3 not configured
> > scsibus1 at sdmmc0: 2 targets, initiator 0
> > sd0 at scsibus1 targ 1 lun 0: <Samsung, MBG4GC, 0000> removable
> > sd0: 29820MB, 512 bytes/sector, 61071360 sectors
> > uvideo0 at uhub0 port 1 configuration 1 interface 0 "04081-00092400F4M2GJ
> > USB Camera" rev 2.00/0.12 addr 2
> > video0 at uvideo0
> > uhub1 at uhub0 port 2 configuration 1 interface 0 "Genesys Logic USB2.0
> > Hub" rev 2.00/32.98 addr 3
> > vscsi0 at root
> > scsibus2 at vscsi0: 256 targets
> > softraid0 at root
> > scsibus3 at softraid0: 256 targets
> > root on sd0a (3e652799893b23ec.a) swap on sd0b dump on sd0b
> > inteldrm0: 1366x768, 32bpp
> > wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
> > wskbd0: connecting to wsdisplay0
> > wsdisplay0: screen 1-5 added (std, vt100 emulation)
> > bwfm0: failed loadfirmware of file brcmfmac43340-sdio.nvram
> > ============================= Sent: Saturday, November 02, 2019 at 11:54
> > PM
> > From: "Brad Smith" <[email protected]>
> > To: "Stefano Enrico Mendola" <[email protected]>
> > Subject: Re: Broadcom firmwares and nvram filesPost a dmesg from the
> > system if you want people to help you.
> > 
> > On 11/2/2019 6:50 PM, Stefano Enrico Mendola wrote:
> > > Hi Folks,
> > > A friend of mine gave me an Asus X250TA to use as a low-power home
> > > server.
> > > I don't want to waste any of the two USB2 ports for an USB-Ethernet
> > > adaptor,
> > > but I'd like to use the integrated wifi module instead.
> > >
> > > After launching fw_update(1) using an USB tethered connection,
> > > the firmware gets installed, but it's apparently lacking a .nvram file.
> > > Here's the error I'm getting:
> > >
> > > $ dmesg | grep brcmbwfm0: failed loadfirmware of file
> > > brcmfmac43340-sdio.nvram The nvram(4) man page is not even that helpful
> > > in this case.
> > > I don't think the OpenBSD philosophy is to throw partially working
> > > firmwares in the repos,
> > > so I think the problem could be related to something else, but I have
> > no
> > > clue what this
> > > something else could be.
> > >
> > > Also, the firmware repository does not contain any .nvram file, which
> > > makes me doubteven more that the problem is related to something
> > > partially supported.
> > >
> > > Any ideas? Best regards
> > > Stefano
> 
> Hi,
> 
> all of the SDIO connected chips need an NVRAM, which can not be provided
> by the firmware image, since it really is a per-device setting.  So one
> could start collecting them, but I'm not sure if that would lead to
> covering most of the machines.  Maybe we could cover at least those that
> complained...
> 
> Anyway, it looks like your NVRAM is stored in a specific EFI variable,
> like it is on some other x86 machines, and since we unfortunately have
> no support for reading EFI variables yet, you will need to use Linux for
> retrieving the file.
> 
> mount -t efivarfs efivarfs /sys/firmware/efi/efivars
> cp /sys/firmware/efi/efivars/nvram-74b00bd9-805a-4d61-b51f-43268123d113 
> /some/external/stick/brcmfmac43340-sdio.txt
> 
> I have attached a program that converts that text file into the nvram
> file:
> 
> cc -o nvram nvram.c
> ./nvram brcmfmac43340-sdio.txt brcmfmac43340-sdio.nvram
> 
> And that's the file you can then put into /etc/firmware to finally use
> your bwfm(4) device!
> 
> Patrick

Obiously I missed the attachment.
/*
 * 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;
}

Reply via email to