tuxsavvy / agere_fw_utils (public) (License: Dual BSD 3-clause and GPLv2) (since 2021-02-07) (hash sha1)
Personal fork of https://repo.or.cz/agere_fw_utils.git

/dump_fw.c (cc75662fce0c5f781e9fc86fbb053de1e062e588) (14347 bytes) (mode 100644) (type blob)

/*
 * Program to link against Agere FW images, and dump contents to
 * binary files for loading directly by linux drivers.
 *
 * Copyright (C) 2008 David Kilroy
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/* Output format (LE numbers)
 *
 * vers        [6] The text HFW and 3 ASCII digits indicating version
 * headersize  [2] Size of header inc vers, headersize and length 
 * entry point [4] NIC address of entry point
 * blocks      [4] Number of blocks (n)
 * blk_offset  [4] Offset to block data
 * pdr_offset  [4] Offset to PDR data
 * pri_offset  [4] Offset to primary plug data
 * cpt_offset  [4] Offset to compatibility data
 * signature [arb] firmware signature
 * Block_1
 * ..
 * Block_n
 * Block_term
 * pda_1
 * ..
 * pda_n
 * pda_term
 * pri_1
 * ..
 * pri_n
 * pri_term
 * compat_1
 * ..
 * compat_n
 * compat_term
 *
 * Where Block_n is:
 *   addr [4]         NIC address to program data
 *   length [2]       Number of bytes of data to program
 *   data [arbitrary] Data to program
 *
 * block term is:
 *   0xFFFFFFFF [4]   BLOCK_END identifier
 *   0x0000 [2]       zero length
 *
 * pda_n and pri_n are:
 *   id [4]   (Primary) PDA identifier
 *   addr [4] Address to program (Primary) PDA
 *   len [4]  Number of bytes to program
 *
 * pda_term and pri_n are:
 *   0x00000000 [4] PDI_END
 *   0x00000000 [4]
 *   0x00000000 [4]
 *
 * compat_n is:
 *   size [2] Length of LTV - ((n_bytes/2) - 1)
 *   code [2] LTV code - 0xFD21 (FW compatibility range)
 *                       0xFD22 (Modem I/F compatibility range)
 *                       0xFD23 (Controller I/F compatibility range)
 *   role [2] Who this restriction applies to?
 *                  0x00 - 'Supplier'
 *                  0x01 - 'Actor'
 *   id  [2]  
 *   spec_1   Specifications
 *   ...
 *   spec_20
 *
 * spec_n is:
 *   variant [2]
 *   bottom [2]
 *   top [2]
 *
 * There is more information available in the driver. In particular
 * whether the block is supposed to be programmed to NV or volatile,
 * and various flags.
 *
 * Apart from the header, the output format is compatible with the
 * spectrum_cs image. The header is arbitrary.
 *
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "dhf.h"

#define AP_SUFFIX "_ap_fw.bin"
#define STA_SUFFIX "_sta_fw.bin"
#define VERSION "HFW000"

/* 0xAABB to 0xBBAA */
#define swap_bytes_16(value)                    \
    ((((value) >> 8) & 0xFF) |                  \
     (((value) & 0xFF) << 8))
/* 0xAABBCCDD to 0xDDCCBBAA */
#define reverse_bytes_32(value)                 \
    ((((value) >> 24) & 0x0000FF) |             \
     (((value) >>  8) & 0x00FF00) |             \
     (((value) <<  8) & 0xFF0000) |             \
     (((value) & 0xFF) << 24))
/* 0xAABBCCDD to 0xBBAADDCC */
#define swap_bytes_32(value)                   \
    ((((value) >>  8) & 0x00FF00FF) |           \
     (((value) <<  8) & 0xFF00FF00))
/* 0xAABBCCDD to 0xCCDDAABB */
#define swap_words_32(value)                    \
    ((((value) >> 16) & 0x0000FFFF) |           \
     (((value) << 16) & 0xFFFF0000))

/*                      address    0    1    2    3 */
/* Pure LE stores 0x12345678 as 0x78 0x56 0x34 0x12 */
/* Pure BE stores 0x12345678 as 0x12 0x34 0x56 0x78 */
/* BEW+LEB stores 0x12345678 as 0x34 0x12 0x78 0x56 */
/* LEW+BEB stores 0x12345678 as 0x56 0x78 0x12 0x34 */
int host_bytes_in_word_be = 0;
int host_words_in_dword_be = 0;

#define host_to_le16(value)                                     \
    (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
#define host_to_le32(value)                                     \
    (host_words_in_dword_be ?                                   \
     (host_bytes_in_word_be ? reverse_bytes_32(value)           \
                            : swap_bytes_32(value)) :           \
     (host_bytes_in_word_be ? swap_words_32(value) : (value)))

#define le16_to_host(value)                                     \
    (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
#define le32_to_host(value)                                     \
    (host_words_in_dword_be ?                                   \
     (host_bytes_in_word_be ? reverse_bytes_32(value)           \
                            : swap_bytes_32(value)) :           \
     (host_bytes_in_word_be ? swap_words_32(value) : (value)))

/* Use C99 exact width types */
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;

/* Checking endianess at runtime because performance isn't an issue,
 * and I'd rather not add a configure step since this is slotting into the
 * Agere source code. */
void check_endianess(void) {
    union {
        u32 dword;
        u16 word[2];
        u8 byte[4];
    } data;
    
    data.dword = 0x12345678;
    if (data.word[0] == 0x1234) {
        host_words_in_dword_be = 1;
    }
    else if (data.word[0] == 0x5678) {
        host_words_in_dword_be = 0;
    } else {
        fprintf(stderr, "Can't determine endianess of host!\n");
        exit(1);
    }

    data.word[0] = 0x1234;
    if (data.byte[0] == 0x12) {
        host_bytes_in_word_be = 1;
    } else if (data.byte[0] == 0x34) {
        host_bytes_in_word_be = 0;
    } else {
        fprintf(stderr, "Can't determine endianess of host!\n");
    }

    if (host_bytes_in_word_be == host_words_in_dword_be) {
        fprintf (stdout, "Detected %s host\n",
                 host_bytes_in_word_be ? "big endian" : "little endian");
    } else {
        fprintf (stdout, "Detected host with mixed endianess\n");
    }
    return;
}

size_t count_blocks(memimage *image)
{
#if __wl_lkm < 718
# define MEMBLOCK memblock
# define BLKSIZE  p->size
# define IF_HAVE_SEGMENT
#else
# define MEMBLOCK CFG_PROG_STRCT
# define BLKSIZE  p->len
# define IF_HAVE_SEGMENT if (p->segment_size)
#endif

    MEMBLOCK *p = image->codep;
    size_t count = 0;
    while (BLKSIZE)
    {
        /* Ignore zero data segments which will not be written */
        IF_HAVE_SEGMENT
        {
            count++;
        }
        p++;
    }
    return count;
}


size_t count_pdr(plugrecord *r)
{
    size_t count = 0;
    if (r)
    {
        while (r->code)
        {
            count++;
            r++;
        }
    }
    return count;
}

size_t acc_block_size(memimage *image)
{
#if __wl_lkm < 718
# define MEMBLOCK memblock
# define BLKSIZE  p->size
# define SEGSIZE  p->size
# define IF_HAVE_SEGMENT
#else
# define MEMBLOCK CFG_PROG_STRCT
# define BLKSIZE  p->len
# define SEGSIZE  p->segment_size
# define IF_HAVE_SEGMENT if (p->segment_size)
#endif

    MEMBLOCK *p = image->codep;
    size_t len = 0;
    while (BLKSIZE)
    {
        /* Ignore zero data segments which will not be written */
        IF_HAVE_SEGMENT
        {
            len += SEGSIZE;
        }
        p++;
    }
    return len;
}

void dump_blocks(FILE* f, memimage *image)
{
#if __wl_lkm < 718
# define MEMBLOCK memblock
# define BLKSIZE  p->size
# define SEGSIZE  p->size
# define NICADDR  p->addr
# define SEGDATA  &(p->data[4]) /* Avoid initial CRC */
# define IF_HAVE_SEGMENT
#else
# define MEMBLOCK CFG_PROG_STRCT
# define BLKSIZE  p->len
# define SEGSIZE  p->segment_size
# define NICADDR  p->nic_addr
# define SEGDATA  p->host_addr
# define IF_HAVE_SEGMENT if (p->segment_size)
#endif

    MEMBLOCK *p = image->codep;
    u8 block_hdr[sizeof(NICADDR) + sizeof(SEGSIZE)];
    u32 *addr = (u32 *) &block_hdr[0];
    u16 *size = (u16 *) &block_hdr[sizeof(NICADDR)];

    while (BLKSIZE)
    {
        IF_HAVE_SEGMENT
        {
            /* There is data to program in this block */
            *addr = host_to_le32(NICADDR);
            *size = host_to_le16(SEGSIZE);
            fwrite (&block_hdr, 1, sizeof(block_hdr), f);
            fwrite (SEGDATA, 1, SEGSIZE, f);
        }
        p++;
    }
    *addr = host_to_le32(0xFFFFFFFFu); /* Agree with spectrum BLOCK_END */
    *size = host_to_le16(0u);

    fwrite (&block_hdr, 1, sizeof(block_hdr), f);

    return;
}

void dump_pdr(FILE *f, plugrecord *r)
{
    u8 pdr[sizeof(r->code) + sizeof(r->addr) + sizeof(r->len)];
    u32 *code = (u32*) &pdr[0];
    u32 *addr = (u32*) &pdr[sizeof(r->code)];
    u32 *len = (u32*) &pdr[sizeof(r->code) + sizeof(r->addr)];

    if (!r)
        goto terminate;

    while (r->code)
    {
        *code = host_to_le32(r->code);
        *addr = host_to_le32(r->addr);
        *len = host_to_le32(r->len);
        fwrite(&pdr, 1, sizeof(pdr), f);
        r++;
    }
terminate:
    /* Terminate the PDR list */
    *code = 0;
    *addr = 0;
    *len = 0;
    fwrite(&pdr, 1, sizeof(pdr), f);
    return;
}

void dump_compat(FILE *f, CFG_RANGE20_STRCT *c)
{
#if __wl_lkm < 718
 #define VARIANT_STRUCT variant
 #define VARIANT_NO number
#else
 #define VARIANT_STRUCT var_rec
 #define VARIANT_NO variant
#endif
    u8 hdr[sizeof(c->id) + sizeof(c->typ) + sizeof(c->role) + sizeof(c->id)];
    u8 spec[sizeof(c->VARIANT_STRUCT[0])];
    u16 *len = (u16*) &hdr[0];
    u16 *typ = (u16*) &hdr[sizeof(c->len)];
    u16 *role = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ)];
    u16 *id = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ) + sizeof(c->role)];
    u16 *variant = (u16*) &spec[0];
    u16 *bottom = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO)];
    u16 *top = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO) + sizeof(c->VARIANT_STRUCT[0].bottom)];
    int i;

    while(c->len)
    {
        *len = host_to_le16(c->len);
        *typ = host_to_le16(c->typ);
        *role = host_to_le16(c->role);
        *id = host_to_le16(c->id);
        fwrite(&hdr, 1, sizeof(hdr), f);

        for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
        {
            *variant = host_to_le16(c->VARIANT_STRUCT[i].VARIANT_NO);
            *bottom = host_to_le16(c->VARIANT_STRUCT[i].bottom);
            *top = host_to_le16(c->VARIANT_STRUCT[i].top);
            fwrite(&spec, 1, sizeof(spec), f);
        }

	c++;
    }

    /* sentinel */
    memset(&hdr[0], 0, sizeof(hdr));
    memset(&spec[0], 0, sizeof(spec));
    fwrite(&hdr, 1, sizeof(hdr), f);
    for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
    {
        fwrite(&spec, 1, sizeof(spec), f);
    }

    return;
}

void dump_image(FILE* f, memimage *image)
{
#if __wl_lkm < 722
 #define PDA pdaplug
 #define PRI priplug
#else
 #define PDA place_holder_1
 #define PRI place_holder_2
#endif
    u32 image_header[6];
    u32 blocks = count_blocks(image);
    u32 blk_offset = 0; /* Immediately after header */
    u32 pdr_offset = (acc_block_size(image) +
                      ((blocks + 1) * (sizeof(u32) + sizeof(u16))));
    u32 pri_offset = pdr_offset +
        ((count_pdr(image->PDA) + 1) * sizeof(u32) * 3);
    u32 cpt_offset = pri_offset +
        ((count_pdr(image->PRI) + 1) * sizeof(u32) * 3);
    u16 headersize = ((sizeof(VERSION)-1) +
                      sizeof(u16) +
                      (sizeof(u32)*6)
#if __wl_lkm >= 718
                      + sizeof(image->signature)
#endif
        );
    u32 *ptr = &image_header[0];
    fwrite (VERSION, 1, sizeof(VERSION)-1, f);
    headersize = host_to_le16(headersize);
    fwrite (&headersize, 1, sizeof(headersize), f);

    *ptr = host_to_le32(image->execution);
    ptr++;
    *ptr = host_to_le32(blocks);
    ptr++;
    *ptr = host_to_le32(blk_offset);
    ptr++;
    *ptr = host_to_le32(pdr_offset);
    ptr++;
    *ptr = host_to_le32(pri_offset);
    ptr++;
    *ptr = host_to_le32(cpt_offset);
    ptr++;
    fwrite (&image_header, 1, sizeof(image_header), f);
#if __wl_lkm >= 718
    fwrite (&image->signature, 1, sizeof(image->signature), f);
#endif

    dump_blocks(f, image);
    dump_pdr(f, image->PDA);
    dump_pdr(f, image->PRI);
    dump_compat(f, image->compat);

    return;
}

#if __wl_lkm < 722
extern memimage ap;
extern memimage station;
#else
extern memimage fw_image;
#define ap fw_image
#define station fw_image
#endif

int main (int argc, char** argv)
{
    char *ap_filename;
    char *sta_filename;
    FILE *ap_file;
    FILE *sta_file;
    size_t len;
    int rc = 0;

    if (argc < 2)
    {
        printf("Please specify a root filename.\n"
               "%s will be appended for primary firmware\n"
               "%s will be appended for secondary firmaware\n",
               AP_SUFFIX, STA_SUFFIX);
        return 1;
    }

    check_endianess();

    len = strlen(argv[1]);

    if (ap.identity->comp_id != COMP_ID_FW_AP)
        goto sta;

    ap_filename = malloc(len + sizeof(AP_SUFFIX) + 1);
    if (!ap_filename)
    {
        fprintf(stderr, "Out of memory\n");
        return 1;
    }
    strncpy(ap_filename, argv[1], len);
    ap_filename[len] = 0;
    strcat(ap_filename, AP_SUFFIX);
    
    ap_file = fopen(ap_filename, "w");
    if (!ap_file)
    {
        fprintf(stderr, "Can't open %s for writing\n", ap_filename);
        rc = 1;
    }
    else
    {
        dump_image(ap_file, &ap);
        fclose(ap_file);
        fprintf (stdout, "Written %s\n", ap_filename);
    }
    free(ap_filename);

sta:
    if (station.identity->comp_id != COMP_ID_FW_STA)
        goto out;

    sta_filename = malloc(len + sizeof(STA_SUFFIX) + 1);
    if (!sta_filename)
    {
        fprintf(stderr, "Out of memory\n");
        return 1;
    }
    strncpy(sta_filename, argv[1], len);
    sta_filename[len] = 0;
    strcat(sta_filename,STA_SUFFIX);

    sta_file = fopen(sta_filename,"w");
    if (!sta_file)
    {
        fprintf(stderr, "Can't open %s for writing\n", sta_filename);
        rc = 1;
    }
    else
    {
        dump_image(sta_file, &station);
        fclose(sta_file);
        fprintf (stdout, "Written %s\n", sta_filename);
    }
    free(sta_filename);

out:
    return rc;
}


Mode Type Size Ref File
100644 blob 4071 5342daffc1d01df12f37bca201bab19d1a3c5a4a LICENSE
100644 blob 1908 5b729e00a22ec5e9837476df2d2d67c610244378 README.dump_fw
100644 blob 6669 bfad7dc7725f545a6b21c5876255c3f2e9c8ee6f README.ubuntu
100644 blob 14347 cc75662fce0c5f781e9fc86fbb053de1e062e588 dump_fw.c
100644 blob 2334 9433d05636b1aaedfcc42325b3e892169ec142ab dump_fw.mk
100644 blob 195 484b779b428de84f3fe27935b17924d547d4be30 hcfcfg.h
100644 blob 36168 f0dc3cd296f43ec1e44d63e54cfada0afcf07e22 hfwget.c
040000 tree - 70f87a1be4df8b8fac9e8263b22a92f388503285 tarballs
040000 tree - 9b5debae8c9a1494b3b14dae3495635b3cc01c4e windows_drivers
040000 tree - dddb518d29a36fbcfa6a75819fdfee1075cd7cd8 wl_lkm_714
040000 tree - 29ae9bc7c2e3608fbaa960a95ef20d56087a0754 wl_lkm_718
040000 tree - 72b5d89d4608987f7499392a4d839a95fda60be7 wl_lkm_722
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/tuxsavvy/agere_fw_utils

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/tuxsavvy/agere_fw_utils

Clone this repository using git:
git clone git://git.rocketgit.com/user/tuxsavvy/agere_fw_utils

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main