github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/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 // Package flash provides higher-level functions such as reading, erasing, 6 // writing and programming the flash chip. 7 package flash 8 9 import ( 10 "encoding/binary" 11 "fmt" 12 "io" 13 14 "github.com/mvdan/u-root-coreutils/pkg/flash/op" 15 "github.com/mvdan/u-root-coreutils/pkg/flash/sfdp" 16 "github.com/mvdan/u-root-coreutils/pkg/spidev" 17 ) 18 19 // sfdpMaxAddress is the highest possible SFDP address (24 bit address space). 20 const sfdpMaxAddress = (1 << 24) - 1 21 22 // SPI interface for the underlying calls to the SPI driver. 23 type SPI interface { 24 Transfer(transfers []spidev.Transfer) error 25 } 26 27 // Flash provides operations for SPI flash chips. 28 type Flash struct { 29 // spi is the underlying SPI device. 30 spi SPI 31 32 // is4ba is true if 4-byte addressing mode is enabled. 33 is4ba bool 34 35 // size is the size of the flash chip in bytes. 36 size int64 37 38 // sfdp is cached. 39 sfdp *sfdp.SFDP 40 41 // JEDEC ID is cached. 42 id uint32 43 44 pageSize int64 45 sectorSize int64 46 blockSize int64 47 } 48 49 // New creates a new flash device from a SPI interface. 50 func New(spi SPI) (*Flash, error) { 51 f := &Flash{ 52 spi: spi, 53 } 54 55 var err error 56 f.sfdp, err = sfdp.Read(f.SFDPReader()) 57 if err != nil { 58 return nil, fmt.Errorf("could not read sfdp: %v", err) 59 } 60 61 density, err := f.SFDP().Param(sfdp.ParamFlashMemoryDensity) 62 if err != nil { 63 return nil, fmt.Errorf("flash chip SFDP does not have density param") 64 } 65 if density >= 0x80000000 { 66 return nil, fmt.Errorf("unsupported flash density: %#x", density) 67 } 68 f.size = (density + 1) / 8 69 70 // Assume 4ba if address if size requires 4 bytes. 71 if f.size >= 0x1000000 { 72 f.is4ba = true 73 } 74 75 // TODO 76 f.pageSize = 256 77 f.sectorSize = 4096 78 f.blockSize = 65536 79 80 return f, nil 81 } 82 83 // Size returns the size of the flash chip in bytes. 84 func (f *Flash) Size() int64 { 85 return f.size 86 } 87 88 const maxTransferSize = 4096 89 90 func min(x, y int64) int64 { 91 if x < y { 92 return x 93 } 94 return y 95 } 96 97 // prepareAddress converts an address to the 3- or 4-byte addressing mode. 98 func (f *Flash) prepareAddress(addr int64) []byte { 99 data := make([]byte, 4) 100 binary.BigEndian.PutUint32(data, uint32(addr)) 101 if f.is4ba { 102 return data 103 } 104 return data[1:] 105 } 106 107 // ReadAt reads from the flash chip. 108 func (f *Flash) ReadAt(p []byte, off int64) (int, error) { 109 // This is a valid implementation of io.ReaderAt. 110 if off < 0 || off > f.size { 111 return 0, io.EOF 112 } 113 p = p[:min(int64(len(p)), f.size-off)] 114 115 // Split the transfer into maxTransferSize chunks. 116 for i := 0; i < len(p); i += maxTransferSize { 117 if err := f.spi.Transfer([]spidev.Transfer{ 118 {Tx: append([]byte{op.Read}, f.prepareAddress(off+int64(i))...)}, 119 {Rx: p[i:min(int64(i)+maxTransferSize, int64(len(p)))]}, 120 }); err != nil { 121 return i, err 122 } 123 } 124 return len(p), nil 125 } 126 127 // writeAt performs a write operation without any care for page sizes or 128 // alignment. 129 func (f *Flash) writeAt(p []byte, off int64) (int, error) { 130 if err := f.spi.Transfer([]spidev.Transfer{ 131 // Enable writing. 132 {Tx: []byte{op.WriteEnable}, CSChange: true}, 133 // Send the address. 134 {Tx: append([]byte{op.PageProgram}, f.prepareAddress(off)...)}, 135 // Send the data. 136 {Tx: p}, 137 }); err != nil { 138 return 0, err 139 } 140 return len(p), nil 141 } 142 143 // WriteAt writes to the flash chip. 144 // 145 // For optimal performance, call this function on boundaries of pageSize. 146 // 147 // NOTE: This will not erase before writing! The ProgramAt function is probably 148 // what you want instead! 149 func (f *Flash) WriteAt(p []byte, off int64) (int, error) { 150 // This is a valid implementation of io.WriterAt. 151 if off < 0 || off > f.size { 152 return 0, io.EOF 153 } 154 p = p[:min(int64(len(p)), f.size-off)] 155 156 // Special case where no page boundaries are crossed. 157 if off%f.pageSize+int64(len(p)) <= f.pageSize { 158 return f.writeAt(p, off) 159 } 160 161 // Otherwise, there are three regions: 162 // 1. A partial page before the first aligned offset. (optional) 163 // 2. All the aligned pages in the middle. 164 // 3. A partial page after the last aligned offset. (optional) 165 firstAlignedOff := (off + f.pageSize - 1) / f.pageSize * f.pageSize 166 lastAlignedOff := (off + int64(len(p))) / f.pageSize * f.pageSize 167 168 if off != firstAlignedOff { 169 if n, err := f.writeAt(p[:firstAlignedOff-off], off); err != nil { 170 return n, err 171 } 172 } 173 for i := firstAlignedOff; i < lastAlignedOff; i += f.pageSize { 174 if _, err := f.writeAt(p[i:i+f.pageSize], off+i); err != nil { 175 return int(i), err 176 } 177 } 178 if off+int64(len(p)) != lastAlignedOff { 179 if _, err := f.writeAt(p[lastAlignedOff-off:], lastAlignedOff); err != nil { 180 return int(lastAlignedOff - off), err 181 } 182 } 183 return len(p), nil 184 } 185 186 func (f *Flash) ProgramAt(p []byte, off int64) (int, error) { 187 // TODO 188 return 0, nil 189 } 190 191 // EraseAt erases n bytes from offset off. Both parameters must be aligned to 192 // sectorSize. 193 func (f *Flash) EraseAt(n int64, off int64) (int64, error) { 194 if off < 0 || off > f.size || off+n > f.size { 195 return 0, io.EOF 196 } 197 198 if (off%f.sectorSize != 0) || (n%f.sectorSize != 0) { 199 return 0, fmt.Errorf("len(p) and off must be multiple of the sector size") 200 } 201 202 for i := int64(0); i < n; { 203 opcode := op.SectorErase 204 eraseSize := f.sectorSize 205 206 // Optimization to erase faster. 207 if i%f.blockSize == 0 && n-i > f.blockSize { 208 opcode = op.BlockErase 209 eraseSize = f.blockSize 210 } 211 212 if err := f.spi.Transfer([]spidev.Transfer{ 213 // Enable writing. 214 { 215 Tx: []byte{op.WriteEnable}, 216 CSChange: true, 217 }, 218 // Send the address. 219 {Tx: append([]byte{opcode}, f.prepareAddress(off+i)...)}, 220 }); err != nil { 221 return i, err 222 } 223 224 i += eraseSize 225 } 226 return n, nil 227 } 228 229 // ReadJEDECID reads the flash chip's JEDEC ID. 230 func (f *Flash) ReadJEDECID() (uint32, error) { 231 tx := []byte{op.ReadJEDECID} 232 rx := make([]byte, 3) 233 234 if err := f.spi.Transfer([]spidev.Transfer{ 235 {Tx: tx}, 236 {Rx: rx}, 237 }); err != nil { 238 return 0, err 239 } 240 241 // Little-endian 242 return (uint32(rx[0]) << 16) | (uint32(rx[1]) << 8) | uint32(rx[2]), nil 243 } 244 245 // SFDPReader is used to read from the SFDP address space. 246 func (f *Flash) SFDPReader() *SFDPReader { 247 return (*SFDPReader)(f) 248 } 249 250 // SFDP returns all the SFDP tables from the flash chip. The value is cached. 251 func (f *Flash) SFDP() *sfdp.SFDP { 252 return f.sfdp 253 } 254 255 // SFDPReader is a wrapper around Flash where the ReadAt function reads from 256 // the SFDP address space. 257 type SFDPReader Flash 258 259 // ReadAt reads from the given offset in the SFDP address space. 260 func (f *SFDPReader) ReadAt(p []byte, off int64) (int, error) { 261 if off < 0 || off > sfdpMaxAddress { 262 return 0, io.EOF 263 } 264 p = p[:min(int64(len(p)), sfdpMaxAddress-off)] 265 tx := []byte{ 266 op.ReadSFDP, 267 // offset, 3-bytes, big-endian 268 byte((off >> 16) & 0xff), byte((off >> 8) & 0xff), byte(off & 0xff), 269 // dummy 0xff 270 0xff, 271 } 272 if err := f.spi.Transfer([]spidev.Transfer{ 273 {Tx: tx}, 274 {Rx: p}, 275 }); err != nil { 276 return 0, err 277 } 278 return len(p), nil 279 }