Skip to content

Commit

Permalink
modularize more functionality to share among UF2 and ELF modules
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Sep 21, 2024
1 parent 56cee47 commit ad6ad41
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 59 deletions.
62 changes: 11 additions & 51 deletions cmd/picobin/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,45 +25,27 @@ func elfinfo(r io.ReaderAt, flags Flags) error {
}
fmt.Printf("total program memory=%d\n", totalSize)

blocks, start, err := getBlocks(f, flags)
blocks, start, err := elfBlocks(f, flags)
if err != nil {
return err
}
addr := start
for i, block := range blocks {
if flags.block >= 0 && i != flags.block {
addr += int64(block.Link)
continue
}

fmt.Printf("\nBLOCK%d @ Addr=%#x Size=%d Items=%d\n", i, addr, block.Size(), len(block.Items))
for _, item := range block.Items {
fmt.Printf("\t%s\n", item.String())
}
addr += int64(block.Link)
}
for i, block := range blocks {
err = block.Validate()
if err != nil {
fmt.Printf("BLOCK%d failed to validate: %s\n", i, err.Error())
}
}
return nil
return blockInfo(blocks, start, flags)
}

func elfdump(r io.ReaderAt, flags Flags) error {
f, err := newElfFile(r, flags)
if err != nil {
return err
}
blocks, start, err := getBlocks(f, flags)
blocks, start, err := elfBlocks(f, flags)
if err != nil {
return err
}

addr := start
for i, block := range blocks {
if flags.block >= 0 && i != flags.block || len(block.Items) >= 1 && block.Items[0].ItemType() == picobin.ItemTypeIgnored {
addr += int64(block.Link)
addr += uint64(block.Link)
continue
}
blockSize := block.Size()
Expand All @@ -72,20 +54,19 @@ func elfdump(r io.ReaderAt, flags Flags) error {
break // Last block.
}
data := make([]byte, dataSize)
n, err := elfutil.ReadAt(f, data, addr+int64(blockSize))
n, err := elfutil.ReadAt(f, data, int64(addr)+int64(blockSize))
if err != nil {
return err
} else if n == 0 {
return errors.New("unable to extract data after block")
}
fmt.Printf("BLOCK%d @ Addr=%#x dump:\n%s", i, addr, hex.Dump(data))
addr += int64(block.Link)
addr += uint64(block.Link)
}
return nil
}

func getBlocks(f *elf.File, flags Flags) ([]picobin.Block, int64, error) {
seenAddrs := make(map[int]struct{})
func elfBlocks(f *elf.File, flags Flags) ([]picobin.Block, uint64, error) {
uromStart, uromEnd, err := elfutil.GetROMAddr(f)
if err != nil {
return nil, 0, err
Expand All @@ -101,32 +82,11 @@ func getBlocks(f *elf.File, flags Flags) ([]picobin.Block, int64, error) {
if err != nil {
return nil, 0, err
}
start0, _, err := picobin.NextBlockIdx(ROM[:flashEnd])
blocks, block0Start, err := romBlocks(ROM[:flashEnd], uromStart)
if err != nil {
return nil, 0, err
}
seenAddrs[start0] = struct{}{}
var blocks []picobin.Block
start := start0
startAbs := int64(start) + romStart
for {
absAddr := int64(start) + romStart
block, _, err := picobin.DecodeBlock(ROM[start:flashEnd])
if err != nil {
return blocks, startAbs, fmt.Errorf("decoding block at Addr=%#x: %w", absAddr, err)
}
blocks = append(blocks, block)
nextStart := start + block.Link
_, alreadySeen := seenAddrs[nextStart]
if alreadySeen {
if nextStart == start0 {
break // Found last block.
}
return blocks, startAbs, fmt.Errorf("odd cyclic block at Addr=%#x", absAddr)
}
seenAddrs[nextStart] = struct{}{}
start = nextStart
return blocks, 0, err
}
startAbs := uint64(block0Start) + uromStart
return blocks, startAbs, nil
}

Expand Down
58 changes: 58 additions & 0 deletions cmd/picobin/picobin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"io"
"log"
"os"

"github.com/github.com/soypat/picobin"
)

const (
Expand Down Expand Up @@ -91,3 +93,59 @@ func run() error {
// Finally run command.
return cmd(file, flags)
}

func romBlocks(ROM []byte, romStartAddr uint64) (blocks []picobin.Block, block0Off int, err error) {
block0Off, _, err = picobin.NextBlockIdx(ROM)
if err != nil {
return nil, 0, err
}
seenAddrs := make(map[int]struct{})
seenAddrs[block0Off] = struct{}{}
start := block0Off
for {
absAddr := uint64(start) + romStartAddr
block, _, err := picobin.DecodeBlock(ROM[start:])
if err != nil {
return blocks, block0Off, fmt.Errorf("decoding block at Addr=%#x: %w", absAddr, err)
}
blocks = append(blocks, block)
nextStart := start + block.Link
_, alreadySeen := seenAddrs[nextStart]
if alreadySeen {
if nextStart == block0Off {
break // Found last block.
}
return blocks, block0Off, fmt.Errorf("odd cyclic block at Addr=%#x", absAddr)
}
seenAddrs[nextStart] = struct{}{}
start = nextStart
}
return blocks, block0Off, nil
}

func blockInfo(blocks []picobin.Block, block0Start uint64, flags Flags) (err error) {
if len(blocks) == 0 {
return errors.New("no blocks found")
}
fmt.Println("ROM Block info:")
addr := block0Start
for i, block := range blocks {
if flags.block >= 0 && i != flags.block {
addr += uint64(block.Link)
continue
}

fmt.Printf("BLOCK%d @ Addr=%#x Size=%d Items=%d\n", i, addr, block.Size(), len(block.Items))
for _, item := range block.Items {
fmt.Printf("\t%s\n", item.String())
}
addr += uint64(block.Link)
}
for i, block := range blocks {
err = block.Validate()
if err != nil {
fmt.Printf("BLOCK%d failed to validate:\n\t%s\n", i, err.Error())
}
}
return nil
}
37 changes: 32 additions & 5 deletions cmd/picobin/uf2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import (
)

func uf2info(r io.ReaderAt, flags Flags) error {
blocks, err := uf2file(r, flags)
uf2blocks, err := uf2file(r, flags)
if err != nil {
return err
}
fmt.Fprintf(os.Stdout, "UF2 %d blocks:\n", len(blocks))
fmt.Fprintf(os.Stdout, "UF2 %d blocks:\n", len(uf2blocks))
h := sha256.New()

for i := range blocks {
block := blocks[i]
for i := range uf2blocks {
block := uf2blocks[i]

data, err := block.Data()
var sum []byte
Expand All @@ -35,7 +35,34 @@ func uf2info(r io.ReaderAt, flags Flags) error {
}
fmt.Fprintf(os.Stdout, "\t%s sha256=%x\n", block.String(), sum)
}
return nil

rd, err := uf2.NewBlocksReaderAt(uf2blocks)
if err != nil {
return err
}
start, end := rd.Addrs()
if end > uint32(flags.flashend) {
end = uint32(flags.flashend)
}
romsize := int(end) - int(start)
if romsize < 0 {
return fmt.Errorf("invalid addresses or bad flash address flag start=%#x, flashlim=%#x", start, flags.flashend)
} else if romsize > int(flags.readsize) {
fmt.Println("limiting ROM read")
romsize = int(flags.readsize)
}
ROM := make([]byte, romsize)
n, err := rd.ReadAt(ROM, int64(start))
if err != nil {
return err
} else if n != len(ROM) {
fmt.Println("unexpected short read:", n, "of", len(ROM))
}
blocks, block0start, err := romBlocks(ROM, uint64(start))
if err != nil {
return err
}
return blockInfo(blocks, uint64(block0start), flags)
}

func uf2conv(r io.ReaderAt, flags Flags) error {
Expand Down
87 changes: 84 additions & 3 deletions uf2/uf2.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,22 @@ func (b Block) HeaderBytes() (hd [32]byte) {

// Data returns a pointer to this block's data field limited to the payload size it has.
func (b *Block) Data() ([]byte, error) {
err := b.Validate()
if err != nil {
return nil, err
}
return b.RawData[:b.PayloadSize], nil
}

// Validate checks block fields for inconsistencies.
func (b *Block) Validate() error {
sz := b.PayloadSize
if sz > BlockMaxData {
return nil, errors.New("payload size exeeds permissible maximum")
return errors.New("payload size exeeds permissible maximum")
} else if sz == 0 {
return nil, errors.New("zero payload size")
return errors.New("zero payload size")
}
return b.RawData[:sz], nil
return nil
}

func DecodeAppendBlocks(dst []Block, r io.Reader, buf []byte) ([]Block, int, error) {
Expand Down Expand Up @@ -249,3 +258,75 @@ func (f *Formatter) forEachBlock(data []byte, targetAddr uint32, fn func(Block)
}
return nil
}

func NewBlocksReaderAt(blocks []Block) (*BlocksReaderAt, error) {
obj := BlocksReaderAt{
blks: blocks,
minAddr: 0xffff_ffff,
}
for i := range blocks {
err := blocks[i].Validate()
if err != nil {
return nil, err
}
obj.minAddr = min(obj.minAddr, blocks[i].TargetAddr)
obj.maxAddr = max(obj.maxAddr, blocks[i].TargetAddr+blocks[i].PayloadSize)
}
return &obj, nil
}

type BlocksReaderAt struct {
blks []Block
minAddr, maxAddr uint32
}

func (blks *BlocksReaderAt) Addrs() (start, end uint32) {
return blks.minAddr, blks.maxAddr
}

func (blks *BlocksReaderAt) ReadAt(b []byte, addr int64) (int, error) {
blocks := blks.blks
end := addr + int64(len(b))
if !aliases(addr, end, int64(blks.minAddr), int64(blks.maxAddr)) {
return 0, io.EOF
}
clear(b)
maxRead := 0
for i := range blocks {
blkAddr := int64(blocks[i].TargetAddr)
blkEnd := blkAddr + int64(blocks[i].PayloadSize)
if !aliases(addr, end, blkAddr, blkEnd) {
continue
}
// progOff := max(0, addr-blkAddr)
bOff := max(0, blkAddr-addr)
n := copy(b[bOff:], blocks[i].RawData[:blocks[i].PayloadSize])
maxRead = int(bOff) + n
}
return maxRead, nil
}

func aliases(start0, end0, start1, end1 int64) bool {
return start0 < end1 && end0 > start1
}

func max[T ~int | ~int64 | ~uint32 | ~uint64](a, b T) T {
if a > b {
return a
}
return b
}

func min[T ~int | ~int64 | ~uint32 | ~uint64](a, b T) T {
if a < b {
return a
}
return b
}

func clear[E any](a []E) {
var z E
for i := range a {
a[i] = z
}
}

0 comments on commit ad6ad41

Please sign in to comment.