github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/contrib/flash/flash.go (about)

     1  // Copyright 2021 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // flash reads and writes to a flash chip.
     6  //
     7  // Synopsis:
     8  //
     9  //	flash -p PROGRAMMER[:parameter[,parameter[...]]] [-r FILE|-w FILE]
    10  //
    11  // Options:
    12  //
    13  //	-p PROGRAMMER: Specify the programmer with zero or more parameters (see
    14  //	               below).
    15  //	-r FILE: Read flash data into the file.
    16  //	-w FILE: Write the file to the flash chip. First, the flash chip is read
    17  //	         and then diffed against the file. The differing blocks are
    18  //	         erased and written. Finally, the contents are verified.
    19  //
    20  // Programmers:
    21  //
    22  //	dummy
    23  //	  Virtual flash programmer for testing in a memory buffer.
    24  //
    25  //	  dummy:image=image.rom
    26  //	    File to memmap for the memory buffer.
    27  //
    28  //	linux_spi:dev=/dev/spidev0.0
    29  //	  Use Linux's spidev driver. This is only supported on Linux. The dev
    30  //	  parameter is required.
    31  //
    32  //	  linux_spi:dev=/dev/spidev0.0,spispeed=5000
    33  //	    Set the SPI controller's speed. The frequency is in kilohertz.
    34  //
    35  // Description:
    36  //
    37  //	flash is u-root's implementation of flashrom. It has a very limited
    38  //	feature set and depends on the the flash chip implementing the SFDP.
    39  package main
    40  
    41  import (
    42  	"errors"
    43  	"fmt"
    44  	"io"
    45  	"log"
    46  	"os"
    47  	"sort"
    48  	"strings"
    49  
    50  	flag "github.com/spf13/pflag"
    51  )
    52  
    53  type programmer interface {
    54  	io.ReaderAt
    55  	io.WriterAt
    56  	Size() int64
    57  	Close() error
    58  }
    59  
    60  type (
    61  	programmerParams map[string]string
    62  	programmerInit   func(programmerParams) (programmer, error)
    63  )
    64  
    65  // supportedProgrammers is populated by the other files in this package.
    66  var supportedProgrammers = map[string]programmerInit{}
    67  
    68  func parseProgrammerParams(arg string) (string, map[string]string) {
    69  	params := map[string]string{}
    70  
    71  	colon := strings.IndexByte(arg, ':')
    72  	if colon == -1 {
    73  		return arg, params
    74  	}
    75  	for _, p := range strings.Split(arg[colon+1:], ",") {
    76  		equal := strings.IndexByte(p, '=')
    77  		if equal == -1 {
    78  			params[p] = ""
    79  			continue
    80  		}
    81  		params[p[:equal]] = p[equal+1:]
    82  	}
    83  	return arg[:colon], params
    84  }
    85  
    86  func run(args []string, supportedProgrammers map[string]programmerInit) (reterr error) {
    87  	// Make a human readable list of supported programmers.
    88  	programmerList := []string{}
    89  	for k := range supportedProgrammers {
    90  		programmerList = append(programmerList, k)
    91  	}
    92  	sort.Strings(programmerList)
    93  
    94  	// Parse args.
    95  	fs := flag.NewFlagSet("flash", flag.ContinueOnError)
    96  	var (
    97  		p = fs.StringP("programmer", "p", "", fmt.Sprintf("programmer (%s)", strings.Join(programmerList, ",")))
    98  		r = fs.StringP("read", "r", "", "read flash data into the file")
    99  		w = fs.StringP("write", "w", "", "write the file to flash")
   100  	)
   101  	if err := fs.Parse(args); err != nil {
   102  		return err
   103  	}
   104  	if fs.NArg() != 0 {
   105  		flag.Usage()
   106  		return errors.New("unexpected positional arguments")
   107  	}
   108  
   109  	if *p == "" {
   110  		return errors.New("-p needs to be set")
   111  	}
   112  
   113  	if *r == "" && *w == "" {
   114  		return errors.New("either -r or -w need to be set")
   115  	}
   116  	if *r != "" && *w != "" {
   117  		return errors.New("both -r and -w cannot be set")
   118  	}
   119  
   120  	programmerName, params := parseProgrammerParams(*p)
   121  	init, ok := supportedProgrammers[programmerName]
   122  	if !ok {
   123  		return fmt.Errorf("unrecognized programmer %q", programmerName)
   124  	}
   125  
   126  	programmer, err := init(params)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	defer func() {
   131  		err := programmer.Close()
   132  		if reterr == nil {
   133  			reterr = err
   134  		}
   135  	}()
   136  
   137  	// Create a buffer to hold the contents of the image.
   138  	buf := make([]byte, programmer.Size())
   139  
   140  	if *r != "" {
   141  		f, err := os.Create(*r)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		defer func() {
   146  			err := f.Close()
   147  			if reterr == nil {
   148  				reterr = err
   149  			}
   150  		}()
   151  		if _, err := programmer.ReadAt(buf, 0); err != nil {
   152  			return err
   153  		}
   154  		if _, err := f.Write(buf); err != nil {
   155  			return err
   156  		}
   157  	} else if *w != "" {
   158  		f, err := os.Open(*w)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		defer f.Close()
   163  		if _, err := io.ReadFull(f, buf); err != nil {
   164  			return err
   165  		}
   166  		if leftover, err := io.Copy(io.Discard, f); err != nil {
   167  			return err
   168  		} else if leftover != 0 {
   169  			return fmt.Errorf("flash size (%#x) unequal to file size (%#x)", len(buf), int64(len(buf))+leftover)
   170  		}
   171  
   172  		return errors.New("write not yet supported")
   173  	}
   174  
   175  	return nil
   176  }
   177  
   178  func main() {
   179  	if err := run(os.Args[1:], supportedProgrammers); err != nil {
   180  		log.Fatalf("Error: %v", err)
   181  	}
   182  }