github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/builder/uf2.go (about) 1 package builder 2 3 // This file converts firmware files from BIN to UF2 format before flashing. 4 // 5 // For more information about the UF2 firmware file format, please see: 6 // https://github.com/Microsoft/uf2 7 // 8 // 9 10 import ( 11 "bytes" 12 "encoding/binary" 13 "os" 14 "strconv" 15 ) 16 17 // convertELFFileToUF2File converts an ELF file to a UF2 file. 18 func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error { 19 // Read the .text segment. 20 targetAddress, data, err := extractROM(infile) 21 if err != nil { 22 return err 23 } 24 25 output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID) 26 if err != nil { 27 return err 28 } 29 return os.WriteFile(outfile, output, 0644) 30 } 31 32 // convertBinToUF2 converts the binary bytes in input to UF2 formatted data. 33 func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) { 34 blocks := split(input, 256) 35 output := make([]byte, 0) 36 37 bl, err := newUF2Block(targetAddr, uf2FamilyID) 38 if err != nil { 39 return nil, 0, err 40 } 41 bl.SetNumBlocks(len(blocks)) 42 43 for i := 0; i < len(blocks); i++ { 44 bl.SetBlockNo(i) 45 bl.SetData(blocks[i]) 46 47 output = append(output, bl.Bytes()...) 48 bl.IncrementAddress(bl.payloadSize) 49 } 50 51 return output, len(blocks), nil 52 } 53 54 const ( 55 uf2MagicStart0 = 0x0A324655 // "UF2\n" 56 uf2MagicStart1 = 0x9E5D5157 // Randomly selected 57 uf2MagicEnd = 0x0AB16F30 // Ditto 58 ) 59 60 // uf2Block is the structure used for each UF2 code block sent to device. 61 type uf2Block struct { 62 magicStart0 uint32 63 magicStart1 uint32 64 flags uint32 65 targetAddr uint32 66 payloadSize uint32 67 blockNo uint32 68 numBlocks uint32 69 familyID uint32 70 data []uint8 71 magicEnd uint32 72 } 73 74 // newUF2Block returns a new uf2Block struct that has been correctly populated 75 func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) { 76 var flags uint32 77 var familyID uint32 78 if uf2FamilyID != "" { 79 flags |= flagFamilyIDPresent 80 v, err := strconv.ParseUint(uf2FamilyID, 0, 32) 81 if err != nil { 82 return nil, err 83 } 84 familyID = uint32(v) 85 } 86 return &uf2Block{magicStart0: uf2MagicStart0, 87 magicStart1: uf2MagicStart1, 88 magicEnd: uf2MagicEnd, 89 targetAddr: targetAddr, 90 flags: flags, 91 familyID: familyID, 92 payloadSize: 256, 93 data: make([]byte, 476), 94 }, nil 95 } 96 97 const ( 98 flagFamilyIDPresent = 0x00002000 99 ) 100 101 // Bytes converts the uf2Block to a slice of bytes that can be written to file. 102 func (b *uf2Block) Bytes() []byte { 103 buf := bytes.NewBuffer(make([]byte, 0, 512)) 104 binary.Write(buf, binary.LittleEndian, b.magicStart0) 105 binary.Write(buf, binary.LittleEndian, b.magicStart1) 106 binary.Write(buf, binary.LittleEndian, b.flags) 107 binary.Write(buf, binary.LittleEndian, b.targetAddr) 108 binary.Write(buf, binary.LittleEndian, b.payloadSize) 109 binary.Write(buf, binary.LittleEndian, b.blockNo) 110 binary.Write(buf, binary.LittleEndian, b.numBlocks) 111 binary.Write(buf, binary.LittleEndian, b.familyID) 112 binary.Write(buf, binary.LittleEndian, b.data) 113 binary.Write(buf, binary.LittleEndian, b.magicEnd) 114 115 return buf.Bytes() 116 } 117 118 // IncrementAddress moves the target address pointer forward by count bytes. 119 func (b *uf2Block) IncrementAddress(count uint32) { 120 b.targetAddr += b.payloadSize 121 } 122 123 // SetData sets the data to be used for the current block. 124 func (b *uf2Block) SetData(d []byte) { 125 b.data = make([]byte, 476) 126 copy(b.data[:], d) 127 } 128 129 // SetBlockNo sets the current block number to be used. 130 func (b *uf2Block) SetBlockNo(bn int) { 131 b.blockNo = uint32(bn) 132 } 133 134 // SetNumBlocks sets the total number of blocks for this UF2 file. 135 func (b *uf2Block) SetNumBlocks(total int) { 136 b.numBlocks = uint32(total) 137 } 138 139 // split splits a slice of bytes into a slice of byte slices of a specific size limit. 140 func split(input []byte, limit int) [][]byte { 141 var block []byte 142 output := make([][]byte, 0, len(input)/limit+1) 143 for len(input) >= limit { 144 // add all blocks 145 block, input = input[:limit], input[limit:] 146 output = append(output, block) 147 } 148 if len(input) > 0 { 149 // add remaining block (that isn't full sized) 150 output = append(output, input) 151 } 152 return output 153 }