mirror of https://github.com/tinygo-org/tinygo.git
wasmstm32webassemblymicrocontrollerarmavrspiwasiadafruitarduinocircuitplayground-expressgpioi2cllvmmicrobitnrf51nrf52nrf52840samd21tinygo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.8 KiB
153 lines
4.8 KiB
package builder
|
|
|
|
// This file implements support for writing ESP image files. These image files
|
|
// are read by the ROM bootloader so have to be in a particular format.
|
|
//
|
|
// In the future, it may be necessary to implement support for other image
|
|
// formats, such as the ESP8266 image formats (again, used by the ROM bootloader
|
|
// to load the firmware).
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"debug/elf"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"sort"
|
|
)
|
|
|
|
type espImageSegment struct {
|
|
addr uint32
|
|
data []byte
|
|
}
|
|
|
|
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
|
|
// ESP8266 chip. This is a special purpose image format just for the ESP chip
|
|
// family, and is parsed by the on-chip mask ROM bootloader.
|
|
//
|
|
// The following documentation has been used:
|
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
|
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
|
|
// https://github.com/espressif/esptool/blob/master/esptool.py
|
|
func makeESPFirmareImage(infile, outfile, format string) error {
|
|
inf, err := elf.Open(infile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer inf.Close()
|
|
|
|
// Load all segments to be written to the image. These are actually ELF
|
|
// sections, not true ELF segments (similar to how esptool does it).
|
|
var segments []*espImageSegment
|
|
for _, section := range inf.Sections {
|
|
if section.Type != elf.SHT_PROGBITS || section.Size == 0 || section.Flags&elf.SHF_ALLOC == 0 {
|
|
continue
|
|
}
|
|
data, err := section.Data()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read section data: %w", err)
|
|
}
|
|
for len(data)%4 != 0 {
|
|
// Align segment to 4 bytes.
|
|
data = append(data, 0)
|
|
}
|
|
if uint64(uint32(section.Addr)) != section.Addr {
|
|
return fmt.Errorf("section address too big: 0x%x", section.Addr)
|
|
}
|
|
segments = append(segments, &espImageSegment{
|
|
addr: uint32(section.Addr),
|
|
data: data,
|
|
})
|
|
}
|
|
|
|
// Sort the segments by address. This is what esptool does too.
|
|
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
|
|
|
// Calculate checksum over the segment data. This is used in the image
|
|
// footer.
|
|
checksum := uint8(0xef)
|
|
for _, segment := range segments {
|
|
for _, b := range segment.data {
|
|
checksum ^= b
|
|
}
|
|
}
|
|
|
|
// Write first to an in-memory buffer, primarily so that we can easily
|
|
// calculate a hash over the entire image.
|
|
// An added benefit is that we don't need to check for errors all the time.
|
|
outf := &bytes.Buffer{}
|
|
|
|
// Image header.
|
|
switch format {
|
|
case "esp32":
|
|
// Header format:
|
|
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
|
|
binary.Write(outf, binary.LittleEndian, struct {
|
|
magic uint8
|
|
segment_count uint8
|
|
spi_mode uint8
|
|
spi_speed_size uint8
|
|
entry_addr uint32
|
|
wp_pin uint8
|
|
spi_pin_drv [3]uint8
|
|
reserved [11]uint8
|
|
hash_appended bool
|
|
}{
|
|
magic: 0xE9,
|
|
segment_count: byte(len(segments)),
|
|
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
|
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
|
|
entry_addr: uint32(inf.Entry),
|
|
wp_pin: 0xEE, // disable WP pin
|
|
hash_appended: true, // add a SHA256 hash
|
|
})
|
|
case "esp8266":
|
|
// Header format:
|
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
|
// Basically a truncated version of the ESP32 header.
|
|
binary.Write(outf, binary.LittleEndian, struct {
|
|
magic uint8
|
|
segment_count uint8
|
|
spi_mode uint8
|
|
spi_speed_size uint8
|
|
entry_addr uint32
|
|
}{
|
|
magic: 0xE9,
|
|
segment_count: byte(len(segments)),
|
|
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
|
spi_speed_size: 0x20, // spi_speed, spi_size: replaced by esptool when flashing
|
|
entry_addr: uint32(inf.Entry),
|
|
})
|
|
default:
|
|
return fmt.Errorf("builder: unknown binary format %#v, expected esp32 or esp8266", format)
|
|
}
|
|
|
|
// Write all segments to the image.
|
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format#segment
|
|
for _, segment := range segments {
|
|
binary.Write(outf, binary.LittleEndian, struct {
|
|
addr uint32
|
|
length uint32
|
|
}{
|
|
addr: segment.addr,
|
|
length: uint32(len(segment.data)),
|
|
})
|
|
outf.Write(segment.data)
|
|
}
|
|
|
|
// Footer, including checksum.
|
|
// The entire image size must be a multiple of 16, so pad the image to one
|
|
// byte less than that before writing the checksum.
|
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
|
outf.WriteByte(checksum)
|
|
|
|
if format == "esp32" {
|
|
// SHA256 hash (to protect against image corruption, not for security).
|
|
hash := sha256.Sum256(outf.Bytes())
|
|
outf.Write(hash[:])
|
|
}
|
|
|
|
// Write the image to the output file.
|
|
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
|
}
|
|
|