github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/flash/sfdp/sfdp.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  // Package sfdp reads the SFDP (Serial Flash Discoverable Parameters) from a
     6  // flash chip where supported. The SFDP is in a separate address space of the
     7  // flash chip and usually read-only. It is used to discover the features
     8  // implemented by the flash chip.
     9  //
    10  // This supports v1.0 of SFDP. Support for v1.5 is incomplete.
    11  //
    12  // Useful references:
    13  // * Your flash chip's datasheet
    14  // * https://chromium.googlesource.com/chromiumos/platform/ec/+/master/include/sfdp.h
    15  // * https://www.macronix.com/Lists/ApplicationNote/Attachments/1870/AN114v1-SFDP%20Introduction.pdf
    16  // * JEDEC specifications
    17  package sfdp
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"io"
    24  	"strings"
    25  )
    26  
    27  const (
    28  	// magic appears as the first 4 bytes of the SFDP table.
    29  	magic = "SFDP"
    30  )
    31  
    32  // Param contains information necessary to lookup a parameter in the SFDP
    33  // tables.
    34  type Param struct {
    35  	Table uint16
    36  	Dword int
    37  	Shift int
    38  	Bits  int
    39  }
    40  
    41  // Parameters from the Basic Table (id=0).
    42  var (
    43  	ParamBlockSectorEraseSize           = Param{0, 0, 0x00, 0x02}
    44  	ParamWriteGranularity               = Param{0, 0, 0x02, 0x01}
    45  	ParamWriteEnableInstructionRequired = Param{0, 0, 0x03, 0x01}
    46  	ParamWriteEnableOpcodeSelect        = Param{0, 0, 0x04, 0x01}
    47  	Param4KBEraseOpcode                 = Param{0, 0, 0x08, 0x08}
    48  	Param112FastRead                    = Param{0, 0, 0x10, 0x01}
    49  	ParamAddressBytesNumberUsed         = Param{0, 0, 0x11, 0x02}
    50  	ParamDoubleTransferRateClocking     = Param{0, 0, 0x13, 0x01}
    51  	Param122FastReadSupported           = Param{0, 0, 0x14, 0x01}
    52  	Param144FastReadSupported           = Param{0, 0, 0x15, 0x01}
    53  	Param114FastReadSupported           = Param{0, 0, 0x16, 0x01}
    54  	ParamFlashMemoryDensity             = Param{0, 1, 0x00, 0x20}
    55  	Param144FastReadNumberOfWaitStates  = Param{0, 2, 0x00, 0x05}
    56  	Param144FastReadNumberOfModeBits    = Param{0, 2, 0x05, 0x03}
    57  	Param144FastReadOpcode              = Param{0, 2, 0x08, 0x08}
    58  	Param114FastReadNumberOfWaitStates  = Param{0, 2, 0x10, 0x05}
    59  	Param114FastReadNumberOfModeBits    = Param{0, 2, 0x15, 0x03}
    60  	Param114FastReadOpcode              = Param{0, 2, 0x18, 0x08}
    61  	Param112FastReadNumberOfWaitStates  = Param{0, 3, 0x00, 0x05}
    62  	Param112FastReadNumberOfModeBits    = Param{0, 3, 0x05, 0x03}
    63  	Param112FastReadOpcode              = Param{0, 3, 0x08, 0x08}
    64  	Param122FastReadNumberOfWaitStates  = Param{0, 3, 0x10, 0x05}
    65  	Param122FastReadNumberOfModeBits    = Param{0, 3, 0x15, 0x03}
    66  	Param122FastReadOpcode              = Param{0, 3, 0x18, 0x08}
    67  	Param222FastReadSupported           = Param{0, 4, 0x00, 0x01}
    68  	Param444FastReadSupported           = Param{0, 4, 0x04, 0x01}
    69  )
    70  
    71  // ParamLookupEntry is a single entry in the BasicTableLookup.
    72  type ParamLookupEntry struct {
    73  	Name  string
    74  	Param Param
    75  }
    76  
    77  // BasicTableLookup maps a textual param name to the Param for debug utilities.
    78  // These are in a separate data structure to facilitate dead code elimination
    79  // and reduce size for programs which do not require this information.
    80  var BasicTableLookup = []ParamLookupEntry{
    81  	{"BlockSectorEraseSize", ParamBlockSectorEraseSize},
    82  	{"WriteGranularity", ParamWriteGranularity},
    83  	{"WriteEnableInstructionRequired", ParamWriteEnableInstructionRequired},
    84  	{"WriteEnableOpcodeSelect", ParamWriteEnableOpcodeSelect},
    85  	{"4KBEraseOpcode", Param4KBEraseOpcode},
    86  	{"112FastRead", Param112FastRead},
    87  	{"AddressBytesNumberUsed", ParamAddressBytesNumberUsed},
    88  	{"DoubleTransferRateClocking", ParamDoubleTransferRateClocking},
    89  	{"122FastReadSupported", Param122FastReadSupported},
    90  	{"144FastReadSupported", Param144FastReadSupported},
    91  	{"114FastReadSupported", Param114FastReadSupported},
    92  	{"FlashMemoryDensity", ParamFlashMemoryDensity},
    93  	{"144FastReadNumberOfWaitStates", Param144FastReadNumberOfWaitStates},
    94  	{"144FastReadNumberOfModeBits", Param144FastReadNumberOfModeBits},
    95  	{"144FastReadOpcode", Param144FastReadOpcode},
    96  	{"114FastReadNumberOfWaitStates", Param114FastReadNumberOfWaitStates},
    97  	{"114FastReadNumberOfModeBits", Param114FastReadNumberOfModeBits},
    98  	{"114FastReadOpcode", Param114FastReadOpcode},
    99  	{"112FastReadNumberOfWaitStates", Param112FastReadNumberOfWaitStates},
   100  	{"112FastReadNumberOfModeBits", Param112FastReadNumberOfModeBits},
   101  	{"112FastReadOpcode", Param112FastReadOpcode},
   102  	{"122FastReadNumberOfWaitStates", Param122FastReadNumberOfWaitStates},
   103  	{"122FastReadNumberOfModeBits", Param122FastReadNumberOfModeBits},
   104  	{"122FastReadOpcode", Param122FastReadOpcode},
   105  	{"222FastReadSupported", Param222FastReadSupported},
   106  	{"444FastReadSupported", Param444FastReadSupported},
   107  }
   108  
   109  // SFDP (Serial Flash Discoverable Parameters) holds a copy of the tables of the SFDP.
   110  //
   111  // The structure is:
   112  //
   113  //	SFDP
   114  //	 |--> Header
   115  //	 \--> []Parameter
   116  //	         |--> ParameterHeader
   117  //	         \--> Table: A copy of the table's contents.
   118  type SFDP struct {
   119  	Header
   120  	Parameters []Parameter
   121  }
   122  
   123  // Header is the header of the SFDP.
   124  type Header struct {
   125  	// Signature is 0x50444653 ("SFDP") if the chip supports SFDP.
   126  	Signature                uint32
   127  	MinorRev                 uint8
   128  	MajorRev                 uint8
   129  	NumberOfParameterHeaders uint8
   130  	_                        uint8
   131  }
   132  
   133  // Parameter holds a single table.
   134  type Parameter struct {
   135  	ParameterHeader
   136  	Table []byte
   137  }
   138  
   139  // ParameterHeader is the header of Parameter.
   140  type ParameterHeader struct {
   141  	IDLSB    uint8
   142  	MinorRev uint8
   143  	MajorRev uint8
   144  	// Length is the number of dwords.
   145  	Length uint8
   146  	// The top byte of Pointer is the MSB of the Id for revision 1.5.
   147  	Pointer uint32
   148  }
   149  
   150  // ID returns the ID for the table. Ths size of the ID depends on the SFDP
   151  // version.
   152  func (p ParameterHeader) ID(sfdpMajorRev, sfdpMinorRev uint8) uint16 {
   153  	id := uint16(p.IDLSB)
   154  	if sfdpMajorRev == 1 && sfdpMinorRev == 5 {
   155  		id |= uint16((p.Pointer >> 16) & 0xff00)
   156  	}
   157  	return id
   158  }
   159  
   160  // UnsupportedError is returned if no SFDP can be found.
   161  type UnsupportedError struct{}
   162  
   163  // Error implements the error interface.
   164  func (*UnsupportedError) Error() string {
   165  	return "could not find an SFDP"
   166  }
   167  
   168  // TableError is returned if the given table id cannot be found.
   169  type TableError struct {
   170  	WantTableID uint16
   171  }
   172  
   173  // Error implements the error interface.
   174  func (e *TableError) Error() string {
   175  	return fmt.Sprintf("could not find table %#x", e.WantTableID)
   176  }
   177  
   178  // DwordError is returned if the given dword index cannot be found in the given table.
   179  type DwordError struct {
   180  	WantTableID uint16
   181  	WantDword   int
   182  }
   183  
   184  // Error implements the error interface.
   185  func (e *DwordError) Error() string {
   186  	return fmt.Sprintf("could not find dword %#x in table %#x", e.WantDword, e.WantTableID)
   187  }
   188  
   189  // Read reads the SFDP and all tables from the flash chip.
   190  func Read(r io.ReaderAt) (*SFDP, error) {
   191  	headerBuf := make([]byte, binary.Size(Header{}))
   192  	if _, err := r.ReadAt(headerBuf, 0); err != nil {
   193  		return nil, err
   194  	}
   195  	if string(headerBuf[:4]) != magic {
   196  		return nil, &UnsupportedError{}
   197  	}
   198  	var header Header
   199  	if err := binary.Read(bytes.NewBuffer(headerBuf), binary.LittleEndian, &header); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	parametersBuf := make([]byte, binary.Size(ParameterHeader{})*int(header.NumberOfParameterHeaders+1))
   204  	if _, err := r.ReadAt(parametersBuf, int64(binary.Size(Header{}))); err != nil {
   205  		return nil, err
   206  	}
   207  	sfdp := &SFDP{
   208  		Header:     header,
   209  		Parameters: make([]Parameter, header.NumberOfParameterHeaders+1),
   210  	}
   211  	for i := range sfdp.Parameters {
   212  		p := &sfdp.Parameters[i]
   213  		if err := binary.Read(bytes.NewBuffer(parametersBuf), binary.LittleEndian, &p.ParameterHeader); err != nil {
   214  			return nil, err
   215  		}
   216  		p.Table = make([]byte, int(p.Length)*4)
   217  		if _, err := r.ReadAt(p.Table, int64(p.Pointer)&0x00ffffff); err != nil {
   218  			return nil, err
   219  		}
   220  	}
   221  	return sfdp, nil
   222  }
   223  
   224  // Dword reads a dword from the SFDP table with the given table id and dword
   225  // index.
   226  func (s *SFDP) Dword(table uint16, dword int) (uint32, error) {
   227  	for _, p := range s.Parameters {
   228  		if p.ID(s.Header.MajorRev, s.Header.MinorRev) == table {
   229  			byteIdx := dword * 4
   230  			if dword < 0 || byteIdx >= len(p.Table) {
   231  				return 0, &DwordError{WantTableID: table, WantDword: dword}
   232  			}
   233  			return uint32(p.Table[byteIdx]) | (uint32(p.Table[byteIdx+1]) << 8) | (uint32(p.Table[byteIdx+2]) << 16) | (uint32(p.Table[byteIdx+3]) << 24), nil
   234  		}
   235  	}
   236  	return 0, &TableError{WantTableID: table}
   237  }
   238  
   239  // Param reads a Param from the SFDP table.
   240  func (s *SFDP) Param(p Param) (int64, error) {
   241  	dword, err := s.Dword(p.Table, p.Dword)
   242  	if err != nil {
   243  		return 0, err
   244  	}
   245  	return (int64(dword) >> p.Shift) & ((1 << int64(p.Bits)) - 1), nil
   246  }
   247  
   248  // PrettyPrint prints each parameter from the lookup in a human-readable format.
   249  func (s *SFDP) PrettyPrint(w io.Writer, l []ParamLookupEntry) error {
   250  	// Get the max width of the param name.
   251  	width := 0
   252  	for _, p := range l {
   253  		if len(p.Name) > width {
   254  			width = len(p.Name)
   255  		}
   256  	}
   257  
   258  	// Print the parameters.
   259  	padding := strings.Repeat(" ", width)
   260  	for _, p := range l {
   261  		val, err := s.Param(p.Param)
   262  		if err == nil {
   263  			_, err := fmt.Fprintf(w, "%s%s %#x\n", p.Name, padding[len(p.Name):], val)
   264  			if err != nil {
   265  				return err
   266  			}
   267  		} else {
   268  			_, err := fmt.Fprintf(w, "%s%s Error: %v\n", p.Name, padding[len(p.Name):], err)
   269  			if err != nil {
   270  				return err
   271  			}
   272  		}
   273  	}
   274  	return nil
   275  }