Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
added dx1elf2dfu utility
Browse files Browse the repository at this point in the history
majbthrd committed Jul 14, 2018
1 parent 9d79a3c commit 83b6ea5
Showing 4 changed files with 523 additions and 1 deletion.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -9,12 +9,20 @@ It is a much more space efficient alternative to the 4kB Atmel/Microchip [AN_423

## Usage

Downloading can be accomplished with the existing [dfu-util](http://dfu-util.sourceforge.net/) utilities:
Downloading can be accomplished with the existing [dfu-util](http://dfu-util.sourceforge.net/) utilities.

Downloading a raw binary file looks like this:

```
dfu-util -D write.bin
```

or by using the provided dx1elf2dfu utility, one can create a .dfu file to be downloaded:

```
dfu-util -D write.dfu
```

## Specifics

The linker memory map of the user application must be modified to have an origin at 0x0000_0400 rather than at 0x0000_0000. This bootloader resides at 0x0000_0000.
14 changes: 14 additions & 0 deletions dx1elf2dfu/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ifeq ($(OS),Windows_NT)
EXE_SUFFIX = .exe
endif

DX1ELF2DFU_C = dx1elf2dfu.c

all: dx1elf2dfu$(EXE_SUFFIX)

dx1elf2dfu$(EXE_SUFFIX): Makefile $(DX1ELF2DFU_C)
gcc $(DX1ELF2DFU_C) -o $@ $(CFLAGS)
strip $@

clean:
rm -f dx1elf2dfu$(EXE_SUFFIX)
11 changes: 11 additions & 0 deletions dx1elf2dfu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
dx1elf2dfu
==========

This tool is a possible aid for developers who use the Dx1bootloader and want to generate a DFU image directly from a Dx1 ELF object file.

## Sample Usage

```
dx1elf2dfu myapp.elf myapp.dfu
```

489 changes: 489 additions & 0 deletions dx1elf2dfu/dx1elf2dfu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,489 @@
/*
command-line tool to convert a SAM Dx1 ELF object file into a DFU image
Copyright (C) 2017,2018 Peter Lawrence
This was written to be used with this bootloader:
https://github.com/majbthrd/SAMDx1-USB-DFU-Bootloader
and thus expects the input ELF to not use the first 1024 bytes.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#include <stdio.h>
#include <stdint.h>
#include <malloc.h>
#include <string.h>

#define USB_VENDOR_ID 0x1209
#define USB_PRODUCT_ID 0x2003

typedef struct Elf32_Ehdr
{
uint8_t e_ident[16];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf32_Ehdr;

typedef struct Elf32_Shdr
{
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
uint32_t sh_addr;
uint32_t sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
} Elf32_Shdr;

typedef struct Elf32_Phdr
{
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;

struct memory_blob
{
uint32_t address, count;
uint8_t *data;
struct memory_blob *next;
};

static Elf32_Ehdr eh;

static uint8_t read_uint8(FILE *fp)
{
uint8_t c;
c = fgetc(fp);
return c;
}

static uint16_t read_uint16(FILE *fp)
{
uint16_t c;
c = fgetc(fp);
c |= fgetc(fp)<<8;
return c;
}

static uint32_t read_uint32(FILE *fp)
{
uint32_t c;
c = fgetc(fp);
c |= fgetc(fp)<<8;
c |= fgetc(fp)<<16;
c |= fgetc(fp)<<24;
return c;
}

static void printEh(void)
{
int i;
printf("e_ident[0] %x\n",eh.e_ident[0]);
printf("e_ident[1-3] ");
for(i=1;i<4;i++)
{
printf("%c",eh.e_ident[i]);
}
printf("\n");
printf("e_ident[4] %x\n",eh.e_ident[4]);
printf("e_ident[5] %x\n",eh.e_ident[5]);
printf("e_ident[6] %x\n",eh.e_ident[6]);
printf("e_ident[7] %x\n",eh.e_ident[7]);
printf("e_type %x\n",eh.e_type);
printf("e_machine %x\n",eh.e_machine);
printf("e_version %x\n",eh.e_version);
printf("e_entry %x\n",eh.e_entry);
printf("e_phoff %x\n",eh.e_phoff);
printf("e_shoff %x\n",eh.e_shoff);
printf("e_flags %x\n",eh.e_flags);
printf("e_ehsize %x\n",eh.e_ehsize);
printf("e_phentsize %x\n",eh.e_phentsize);
printf("e_phnum %x\n",eh.e_phnum);
printf("e_shentsize %x\n",eh.e_shentsize);
printf("e_shnum %x\n",eh.e_shnum);
printf("e_shstrndx %x\n",eh.e_shstrndx);
}

void readEh(FILE *fp)
{
int i;
for(i=0;i<16;i++)
{
eh.e_ident[i]=read_uint8(fp);
}
eh.e_type = read_uint16(fp);
eh.e_machine = read_uint16(fp);
eh.e_version = read_uint32(fp);
eh.e_entry = read_uint32(fp);
eh.e_phoff = read_uint32(fp);
eh.e_shoff = read_uint32(fp);
eh.e_flags = read_uint32(fp);
eh.e_ehsize = read_uint16(fp);
eh.e_phentsize = read_uint16(fp);
eh.e_phnum = read_uint16(fp);
eh.e_shentsize = read_uint16(fp);
eh.e_shnum = read_uint16(fp);
eh.e_shstrndx = read_uint16(fp);
}

static int checkEh(void)
{
int error = 0;
const uint8_t ref_ident[7] = { 0x7F, 'E', 'L', 'F', 1, 1, 1 };

if (memcmp(eh.e_ident, ref_ident, sizeof(ref_ident)))
{
error = 1;
printf("ERROR: wrong e_ident\n");
}
if (eh.e_type != 2)
{
error = 1;
printf("ERROR: wrong e_type 0x%x != 0x2\n",eh.e_type);
}
if (eh.e_machine != 40)
{
error = 1;
printf("ERROR: wrong e_machine 0x%x != 0x28\n",eh.e_machine);
}
if (eh.e_version != 1)
{
error = 1;
printf("ERROR: wrong e_version 0x%x != 0x1\n",eh.e_version);
}
return error;
}

static Elf32_Shdr readSh(int num,FILE *fp)
{
Elf32_Shdr sh;

fseek(fp,num*eh.e_shentsize+eh.e_shoff,SEEK_SET);
sh.sh_name = read_uint32(fp);
sh.sh_type = read_uint32(fp);
sh.sh_flags = read_uint32(fp);
sh.sh_addr = read_uint32(fp);
sh.sh_offset = read_uint32(fp);
sh.sh_size = read_uint32(fp);
sh.sh_link = read_uint32(fp);
sh.sh_info = read_uint32(fp);
sh.sh_addralign = read_uint32(fp);
sh.sh_entsize = read_uint32(fp);

return sh;
}

static Elf32_Phdr readPh(int num,FILE *fp)
{
Elf32_Phdr ph;

fseek(fp,num*eh.e_phentsize+eh.e_phoff,SEEK_SET);
ph.p_type = read_uint32(fp);
ph.p_offset = read_uint32(fp);
ph.p_vaddr = read_uint32(fp);
ph.p_paddr = read_uint32(fp);
ph.p_filesz = read_uint32(fp);
ph.p_memsz = read_uint32(fp);
ph.p_flags = read_uint32(fp);
ph.p_align = read_uint32(fp);

return ph;
}

static struct memory_blob *find_blob(uint32_t address, uint32_t count, struct memory_blob **list)
{
struct memory_blob *current, *previous, *addition;

current = *list; previous = NULL;
while (current)
{
if (current->address > address)
break;

previous = current;
current = current->next;
}

addition = malloc(sizeof(struct memory_blob));
memset(addition, 0, sizeof(struct memory_blob));

addition->data = malloc(count);
addition->address = address;
addition->count = count;
addition->next = current;

if (previous)
previous->next = addition;
else
*list = addition;

return addition;
}

static const uint32_t crc32_table[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};

static uint32_t crc32_calc(uint32_t crc, uint8_t *buffer, uint32_t length)
{
while (length--)
crc = crc32_table[(crc ^ *buffer++) & 0xff] ^ (crc >> 8);

return crc;
}

int main(int argc, char *argv[])
{
FILE *elffp;
FILE *dfufp;
int i, j;
Elf32_Phdr *ph;
Elf32_Shdr sh;
struct memory_blob *blob, *pm_list;
uint32_t phy_addr, origin_addr, crc32, stuff_size;
uint8_t scratchpad[64 /* sized to be at least as large as the DFU suffix */];

if (argc < 3)
{
printf("%s <input.elf> <output.dfu>\n", argv[0]);
return -1;
}

elffp = fopen(argv[1], "rb");
if (!elffp)
{
printf("ERROR: unable to open file <%s> for reading\n", argv[1]);
return -1;
}

/*
read (and check) ELF header
*/

readEh(elffp);
if (checkEh())
{
printEh();
return -1;
}

/*
read ELF Program Headers
*/

ph = (Elf32_Phdr *)malloc(eh.e_phnum * sizeof(Elf32_Phdr));
for(i=0;i<eh.e_phnum;i++)
{
ph[i] = readPh(i,elffp);
}

/*
read ELF Section Headers, and add relevant sections (re-mapped to physical address) to blob list
*/

pm_list = NULL;
for(i=0;i<eh.e_shnum;i++)
{
sh = readSh(i,elffp);

if ( (sh.sh_type==1) && (sh.sh_flags & 0x6) )
{
for (j = 0; j < eh.e_phnum; j++)
{
if ( (sh.sh_addr >= ph[j].p_vaddr) && (sh.sh_addr < (ph[j].p_vaddr + ph[j].p_memsz)) && ph[j].p_filesz )
{
phy_addr = ph[j].p_paddr + (sh.sh_addr - ph[j].p_vaddr);
blob = find_blob(phy_addr, sh.sh_size, &pm_list);

fseek(elffp, sh.sh_offset, SEEK_SET);
fread(blob->data, blob->count, 1, elffp);
break;
}
}
}
}

/* we've read everything we need from the ELF file, so we can close the file */
fclose(elffp);

/*
write blob list to raw file
*/

blob = pm_list;

if (!blob)
{
printf("ERROR: nothing useable in ELF; DFU cannot be created\n");
return -1;
}

origin_addr = 0x400; /* this is the first address available after the bootloader */
crc32 = 0xFFFFFFFF;

if (blob->address < origin_addr)
{
printf("ERROR: provided ELF intrudes into bootloader space; DFU cannot be created\n");
return -1;
}

dfufp = fopen(argv[2], "wb");
if (!dfufp)
{
printf("ERROR: unable to open file <%s> for writing\n", argv[2]);
return -1;
}

while (blob)
{
phy_addr = blob->address;

while (origin_addr < phy_addr)
{
memset(scratchpad, 0xFF, sizeof(scratchpad));
stuff_size = phy_addr - origin_addr;
if (stuff_size > sizeof(scratchpad))
stuff_size = sizeof(scratchpad);
crc32 = crc32_calc(crc32, scratchpad, stuff_size);
fwrite(scratchpad, stuff_size, 1, dfufp);
origin_addr += stuff_size;
}

crc32 = crc32_calc(crc32, blob->data, blob->count);
fwrite(blob->data, blob->count, 1, dfufp);
origin_addr += blob->count;
blob = blob->next;
}

/*
append DFU standard suffix
*/

i = 0;
scratchpad[i++] = 0xFF; // bcdDevice
scratchpad[i++] = 0xFF;
scratchpad[i++] = (uint8_t)(USB_PRODUCT_ID >> 0); // idProduct
scratchpad[i++] = (uint8_t)(USB_PRODUCT_ID >> 8);
scratchpad[i++] = (uint8_t)(USB_VENDOR_ID >> 0); // idVendor
scratchpad[i++] = (uint8_t)(USB_VENDOR_ID >> 8);
scratchpad[i++] = 0x00; // bcdDFU
scratchpad[i++] = 0x01;
scratchpad[i++] = 'U'; // ucDfuSignature
scratchpad[i++] = 'F';
scratchpad[i++] = 'D';
scratchpad[i++] = 16; // bLength

/* the CRC-32 has now been calculated over the entire file, save for the CRC field itself */
crc32 = crc32_calc(crc32, scratchpad, i);

scratchpad[i++] = (uint8_t)(crc32 >> 0);
scratchpad[i++] = (uint8_t)(crc32 >> 8);
scratchpad[i++] = (uint8_t)(crc32 >> 16);
scratchpad[i++] = (uint8_t)(crc32 >> 24);

fwrite(scratchpad, i, 1, dfufp);

fclose(dfufp);

return 0;
}

0 comments on commit 83b6ea5

Please sign in to comment.