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 }