1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using Antmicro.Renode.Core; 9 using Antmicro.Renode.Core.Extensions; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Peripherals.Memory; 14 using Antmicro.Renode.Utilities; 15 using System; 16 using System.Linq; 17 using System.Collections.Generic; 18 using Endianess = ELFSharp.ELF.Endianess; 19 using static Antmicro.Renode.Utilities.BitHelper; 20 using System.IO; 21 22 namespace Antmicro.Renode.Peripherals.MTD 23 { 24 public sealed class AMDCFIFlash : IBytePeripheral, IWordPeripheral, IDoubleWordPeripheral, IKnownSize, IDisposable 25 { AMDCFIFlash(IMachine machine, MappedMemory underlyingMemory, int interleave = 4, ushort manufacturerId = 2, ushort alternateManufacturerId = 0, uint deviceId = 0x1234)26 public AMDCFIFlash(IMachine machine, MappedMemory underlyingMemory, int interleave = 4, 27 ushort manufacturerId = 2, ushort alternateManufacturerId = 0, uint deviceId = 0x1234) 28 { 29 var sysbus = machine.GetSystemBus(this); 30 // We need to consider the bus endianness for the reads that we pass through to the memory peripheral. 31 if(sysbus.Endianess == Endianess.BigEndian) 32 { 33 readMemoryWord = underlyingMemory.ReadWordBigEndian; 34 readMemoryDoubleWord = underlyingMemory.ReadDoubleWordBigEndian; 35 writeMemoryWord = underlyingMemory.WriteWordBigEndian; 36 writeMemoryDoubleWord = underlyingMemory.WriteDoubleWordBigEndian; 37 } 38 else 39 { 40 readMemoryWord = underlyingMemory.ReadWordBigEndian; 41 readMemoryDoubleWord = underlyingMemory.ReadDoubleWordBigEndian; 42 writeMemoryWord = underlyingMemory.WriteWordBigEndian; 43 writeMemoryDoubleWord = underlyingMemory.WriteDoubleWordBigEndian; 44 } 45 this.underlyingMemory = underlyingMemory; 46 this.interleave = interleave; 47 this.manufacturerId = manufacturerId; 48 this.alternateManufacturerId = alternateManufacturerId; 49 this.deviceId = deviceId; 50 bankSectorSizes = new List<List<long>> 51 { 52 Enumerable.Repeat(0x2000L, 8).Concat(Enumerable.Repeat(0x10000L, 15)).ToList(), 53 Enumerable.Repeat(0x10000L, 48).ToList(), 54 Enumerable.Repeat(0x10000L, 48).ToList(), 55 Enumerable.Repeat(0x10000L, 15).Concat(Enumerable.Repeat(0x2000L, 8)).ToList(), 56 }; 57 RecalculateSizes(); 58 Reset(); 59 } 60 61 // Loading happens immediately, saving at dispose time. AddBackingFile(ReadFilePath path, long offset, int size, bool read = true, bool write = false)62 public void AddBackingFile(ReadFilePath path, long offset, int size, bool read = true, bool write = false) 63 { 64 if(read) 65 { 66 try 67 { 68 if(size + offset > Size) 69 { 70 throw new ArgumentException($"Loaded size ({size} bytes) plus offset ({offset}) exceeds memory size ({Size} bytes)"); 71 } 72 this.DebugLog("Loading {0} bytes at 0x{1:x} from {2}", size, offset, path); 73 byte[] bytes; 74 using(var stream = File.OpenRead(path)) 75 { 76 using(var reader = new BinaryReader(stream)) 77 { 78 bytes = reader.ReadBytes(size); 79 } 80 } 81 if(bytes.Length < size) 82 { 83 this.WarningLog("Wanted to read {0} bytes from '{1}', but got only {2}", size, path, bytes.Length); 84 } 85 underlyingMemory.WriteBytes(offset, bytes); 86 } 87 catch(Exception e) 88 { 89 throw new RecoverableException($"Failed to load data from file: {e.Message}", e); 90 } 91 } 92 93 if(write) 94 { 95 backingFiles.Add(offset, new BackingFile(size, path)); 96 } 97 } 98 99 // Used to define a custom flash layout in the repl/resc. The default one is equivalent to: 100 // ClearSectorSizes 101 // AddSectorSizes 0x2000 8 bank=0 102 // AddSectorSizes 0x10000 15 bank=0 103 // AddSectorSizes 0x10000 48 bank=1 104 // AddSectorSizes 0x10000 48 bank=2 105 // AddSectorSizes 0x10000 15 bank=3 106 // AddSectorSizes 0x2000 8 bank=3 107 // The banks must be in sequential order, and ClearSectorSizes should be used to clear the old layout first. ClearSectorSizes()108 public void ClearSectorSizes() 109 { 110 bankSectorSizes = new List<List<long>>(); 111 } 112 AddSectorSizes(long size, int repeat, int bank)113 public void AddSectorSizes(long size, int repeat, int bank) 114 { 115 var lastBank = bankSectorSizes.Count - 1; 116 var entry = Enumerable.Repeat(size, repeat); 117 if(bank != lastBank) 118 { 119 if(bank != lastBank + 1) 120 { 121 throw new RecoverableException($"Banks must be added in sequential order (expected {lastBank + 1})"); 122 } 123 bankSectorSizes.Add(entry.ToList()); 124 } 125 else 126 { 127 bankSectorSizes[bank].AddRange(entry); 128 } 129 130 RecalculateSizes(); 131 } 132 Reset()133 public void Reset() 134 { 135 state = State.ReadArray; 136 unlockCycle = 0; 137 } 138 Dispose()139 public void Dispose() 140 { 141 foreach(var fileOffset in backingFiles) 142 { 143 var offset = fileOffset.Key; 144 var file = fileOffset.Value; 145 this.DebugLog("Saving {0} bytes at 0x{1:x} to {2}", file.size, offset, file.path); 146 try 147 { 148 File.WriteAllBytes(file.path, underlyingMemory.ReadBytes(offset, file.size)); 149 } 150 catch(Exception e) 151 { 152 throw new RecoverableException($"Failed to save data to file: {e.Message}", e); 153 } 154 } 155 } 156 ReadByte(long offset)157 public byte ReadByte(long offset) 158 { 159 if(state == State.ReadArray) 160 { 161 return underlyingMemory.ReadByte(offset); 162 } 163 if(state == State.ReadQuery) 164 { 165 // No alignment requirement, in fact for example for interleave = 4 it's expected that 166 // ReadByte(0x10..0x13) gives the same result - all the chips get the same value on their 167 // address lines and so the CFI space looks like every byte is repeated 4 times 168 return HandleQuery(offset / interleave); 169 } 170 if(state == State.ReadAutoselect) 171 { 172 // The same applies for the autoselect data 173 return HandleAutoselect(offset / interleave); 174 } 175 176 this.WarningLog("ReadByte in unexpected state {0}", state); 177 return 0; 178 } 179 WriteByte(long offset, byte value)180 public void WriteByte(long offset, byte value) 181 { 182 if(state == State.ProgramWord && Unlocked) 183 { 184 underlyingMemory.WriteByte(offset, value); 185 state = State.ReadArray; 186 unlockCycle = 0; 187 return; 188 } 189 if(state == State.SectorEraseSetup && Unlocked) 190 { 191 var sectorIdx = Array.BinarySearch(sectorStarts, offset); 192 if(sectorIdx < 0) 193 { 194 this.WarningLog("Not erasing at unaligned address 0x{0:x}", offset); 195 } 196 else if(value != (byte)Command.SectorErase) 197 { 198 this.WarningLog("Unexpected byte written in ready-to-erase state: 0x{0:x2}", value); 199 } 200 else 201 { 202 var sectorSize = sectorSizes[sectorIdx]; 203 underlyingMemory.SetRange(offset, sectorSize, ErasedValue); 204 this.DebugLog("Erased {0}-byte sector at 0x{1:x}", sectorSize, offset); 205 } 206 state = State.ReadArray; 207 unlockCycle = 0; 208 return; 209 } 210 211 this.DebugLog("Handling command {0} (0x{1:x})", (Command)value, value); 212 switch((Command)value) 213 { 214 case Command.ReadQuery: 215 state = State.ReadQuery; 216 break; 217 case Command.ReadAutoselect: 218 state = State.ReadAutoselect; 219 break; 220 case Command.Reset: 221 case Command.ReadArray: 222 state = State.ReadArray; 223 break; 224 case Command.UnlockCycle1: 225 unlockCycle = unlockCycle == 0 ? 1 : 0; 226 break; 227 case Command.UnlockCycle2: 228 unlockCycle = unlockCycle == 1 ? 2 : 0; 229 break; 230 case Command.ProgramWord: 231 state = Unlocked ? State.ProgramWord : State.ReadArray; 232 break; 233 case Command.SectorEraseSetup: 234 // To allow setting up an erase operation, the device needs to be unlocked, 235 if(Unlocked) 236 { 237 state = State.SectorEraseSetup; 238 // and another unlock sequence will be required to actually perform the erase. 239 unlockCycle = 0; 240 } 241 break; 242 default: 243 this.WarningLog("Unknown command 0x{0:x}", value); 244 break; 245 } 246 } 247 ReadWord(long offset)248 public ushort ReadWord(long offset) 249 { 250 if(state == State.ReadArray) 251 { 252 return readMemoryWord(offset); 253 } 254 return this.ReadWordUsingByte(offset); 255 } 256 WriteWord(long offset, ushort value)257 public void WriteWord(long offset, ushort value) 258 { 259 if(state == State.ProgramWord && Unlocked) 260 { 261 writeMemoryWord(offset, value); 262 state = State.ReadArray; 263 unlockCycle = 0; 264 return; 265 } 266 this.WriteWordUsingByte(offset, value); 267 } 268 ReadDoubleWord(long offset)269 public uint ReadDoubleWord(long offset) 270 { 271 if(state == State.ReadArray) 272 { 273 return readMemoryDoubleWord(offset); 274 } 275 return this.ReadDoubleWordUsingByte(offset); 276 } 277 WriteDoubleWord(long offset, uint value)278 public void WriteDoubleWord(long offset, uint value) 279 { 280 if(state == State.ProgramWord && Unlocked) 281 { 282 writeMemoryDoubleWord(offset, value); 283 state = State.ReadArray; 284 unlockCycle = 0; 285 return; 286 } 287 this.WriteDoubleWordUsingByte(offset, value); 288 } 289 290 public long Size => underlyingMemory.Size; 291 PrepareCfiData()292 private byte[] PrepareCfiData() 293 { 294 return new VariableLengthValue(0x5c * 8) 295 .DefineFragment(0x10 * 8, 8, (byte)'Q') 296 .DefineFragment(0x11 * 8, 8, (byte)'R') 297 .DefineFragment(0x12 * 8, 8, (byte)'Y') 298 .DefineFragment(0x13 * 8, 16, manufacturerId, name: "Manufacturer ID") 299 .DefineFragment(0x15 * 8, 16, 0x0040, name: "Starting Address for the Primary Vendor-Specific Extended Query Table") 300 .DefineFragment(0x17 * 8, 16, alternateManufacturerId, name: "Alternate manufacturer ID") 301 .DefineFragment(0x19 * 8, 16, 0x0000, name: "Starting Address for the Alternate Vendor-Specific Extended Query Table") 302 .DefineFragment(0x1B * 8, 8, 0x27, name: "V_cc lower limit") 303 .DefineFragment(0x1C * 8, 8, 0x36, name: "V_cc upper limit") 304 .DefineFragment(0x1D * 8, 8, 0x00, name: "V_pp lower limit") 305 .DefineFragment(0x1E * 8, 8, 0x00, name: "V_pp upper limit") 306 .DefineFragment(0x1F * 8, 8, 0x03, name: "Typical word programming time") 307 .DefineFragment(0x20 * 8, 8, 0x00, name: "Typical buffer programming time") 308 .DefineFragment(0x21 * 8, 8, 0x09, name: "Typical sector erase time") 309 .DefineFragment(0x22 * 8, 8, 0x0F, name: "Typical chip erase time") 310 .DefineFragment(0x23 * 8, 8, 0x04, name: "Maximum word programming time") 311 .DefineFragment(0x24 * 8, 8, 0x00, name: "Maximum buffer programming time") 312 .DefineFragment(0x25 * 8, 8, 0x04, name: "Maximum sector erase time") 313 .DefineFragment(0x26 * 8, 8, 0x00, name: "Maximum chip erase time") 314 .DefineFragment(0x27 * 8, 8, (ulong)Math.Log(Size, 2), name: "Base-2 logarithm of device size") 315 .DefineFragment(0x28 * 8, 8, 0x0002, name: "Flash Device Interface Code description") 316 .DefineFragment(0x2A * 8, 16, 0x0000, name: "Base-2 logarithm of the maximum number of bytes in a multi-byte program") 317 .DefineFragment(0x2C * 8, 8, (ulong)sectorRegions.Length, name: "Number of erase sector regions within device") 318 .DefineFragment(0x2D * 8, 16, (sectorRegions.ElementAtOrDefault(0)?.count ?? 1) - 1, name: "Number of sectors in region 1") 319 .DefineFragment(0x2F * 8, 16, (sectorRegions.ElementAtOrDefault(0)?.size ?? 0UL) / 256, name: "Size of sector in region 1") 320 .DefineFragment(0x31 * 8, 16, (sectorRegions.ElementAtOrDefault(1)?.count ?? 1) - 1, name: "Number of sectors in region 2") 321 .DefineFragment(0x33 * 8, 16, (sectorRegions.ElementAtOrDefault(1)?.size ?? 0UL) / 256, name: "Size of sector in region 2") 322 .DefineFragment(0x35 * 8, 16, (sectorRegions.ElementAtOrDefault(2)?.count ?? 1) - 1, name: "Number of sectors in region 3") 323 .DefineFragment(0x37 * 8, 16, (sectorRegions.ElementAtOrDefault(2)?.size ?? 0UL) / 256, name: "Size of sector in region 3") 324 .DefineFragment(0x39 * 8, 16, (sectorRegions.ElementAtOrDefault(3)?.count ?? 1) - 1, name: "Number of sectors in region 4") 325 .DefineFragment(0x3B * 8, 16, (sectorRegions.ElementAtOrDefault(3)?.size ?? 0UL) / 256, name: "Size of sector in region 4") 326 .DefineFragment(0x40 * 8, 8, (byte)'P') 327 .DefineFragment(0x41 * 8, 8, (byte)'R') 328 .DefineFragment(0x42 * 8, 8, (byte)'I') 329 .DefineFragment(0x43 * 8, 8, (byte)'1', name: "Major version number, ASCII (reflects modifications to the silicon)") 330 .DefineFragment(0x44 * 8, 8, (byte)'3', name: "Major version number, ASCII (reflects modifications to the CFI table)") 331 .DefineFragment(0x45 * 8, 2, 0b00, name: "Address sensitive unlock") 332 .DefineFragment(0x45 * 8 + 2, 6, 0b110000, name: "Process technology") 333 .DefineFragment(0x46 * 8, 8, 0x02, name: "Erase suspend") 334 .DefineFragment(0x47 * 8, 8, 0x01, name: "Sector protect") 335 .DefineFragment(0x48 * 8, 8, 0x01, name: "Temporary sector unprotect") 336 .DefineFragment(0x49 * 8, 8, 0x04, name: "Sector protection scheme") 337 .DefineFragment(0x4A * 8, 8, 0x07, name: "Simultaneous operation") 338 .DefineFragment(0x4B * 8, 8, 0x00, name: "Burst mode type") 339 .DefineFragment(0x4C * 8, 8, 0x00, name: "Page mode type") 340 .DefineFragment(0x4D * 8, 8, 0x00, name: "Acceleration power supply voltage lower limit") 341 .DefineFragment(0x4E * 8, 8, 0x00, name: "Acceleration power supply voltage lower limit") 342 .DefineFragment(0x4F * 8, 8, 0x01, name: "Sector layout (uniform / 8x8 KiB top/bottom)") 343 .DefineFragment(0x50 * 8, 8, 0x00, name: "Program suspend") 344 .DefineFragment(0x57 * 8, 8, (ulong)bankSectorSizes.Count, name: "Number of banks") 345 .DefineFragment(0x58 * 8, 8, (ulong)(bankSectorSizes.ElementAtOrDefault(0)?.Count ?? 0), name: "Number of sectors in bank 1") 346 .DefineFragment(0x59 * 8, 8, (ulong)(bankSectorSizes.ElementAtOrDefault(1)?.Count ?? 0), name: "Number of sectors in bank 2") 347 .DefineFragment(0x5A * 8, 8, (ulong)(bankSectorSizes.ElementAtOrDefault(2)?.Count ?? 0), name: "Number of sectors in bank 3") 348 .DefineFragment(0x5B * 8, 8, (ulong)(bankSectorSizes.ElementAtOrDefault(3)?.Count ?? 0), name: "Number of sectors in bank 4") 349 .Bits.AsByteArray(); 350 } 351 PrepareAutoselectData()352 private byte[] PrepareAutoselectData() 353 { 354 return new VariableLengthValue(0x1f * 8) 355 .DefineFragment(0x00 * 8, 8, (ulong)(manufacturerId & 0xff), name: "Manufacturer ID") 356 .DefineFragment(0x02 * 8, 8, (deviceId >> 16) & 0xff, name: "Device ID 1") 357 .DefineFragment(0x1c * 8, 8, (deviceId >> 8) & 0xff, name: "Device ID 2") 358 .DefineFragment(0x1e * 8, 8, deviceId & 0xff, name: "Device ID 3") 359 .Bits.AsByteArray(); 360 } 361 HandleQuery(long offset)362 private byte HandleQuery(long offset) 363 { 364 // This division is to use sequential (byte mode) offsets in the data definition 365 var byteOffset = offset / 2; 366 if(byteOffset >= cfiData.Length) 367 { 368 this.WarningLog("Unhandled query read at offset 0x{0:x}, returning 0", offset); 369 return 0; 370 } 371 return cfiData[byteOffset]; 372 } 373 HandleAutoselect(long offset)374 private byte HandleAutoselect(long offset) 375 { 376 if(offset >= autoselectData.Length) 377 { 378 this.WarningLog("Unhandled autoselect read at offset 0x{0:x}, returning 0", offset); 379 return 0; 380 } 381 return cfiData[offset]; 382 } 383 RecalculateSizes()384 private void RecalculateSizes() 385 { 386 sectorSizes = bankSectorSizes.SelectMany(bankSizes => bankSizes.Select(s => s * interleave)).ToArray(); 387 sectorStarts = Enumerable.Concat(new [] { 0L }, Misc.Prefix(sectorSizes, (a, b) => a + b)).ToArray(); 388 // A sector region is a run of contiguous sectors with the same size, including across banks. 389 // We divide the sizes in here by the number of chips in parallel as they are used in CFI data, and that of 390 // course needs to contain the block size for *one* chip. 391 sectorRegions = sectorSizes.Aggregate(new List<SectorRegion>(), (regions, size) => 392 { 393 if(regions.LastOrDefault()?.size == (ulong)(size / interleave)) 394 { 395 regions.Last().count++; 396 } 397 else 398 { 399 regions.Add(new SectorRegion((ulong)(size / interleave), 1)); 400 } 401 return regions; 402 }).ToArray(); 403 404 cfiData = PrepareCfiData(); 405 autoselectData = PrepareAutoselectData(); 406 } 407 408 private bool Unlocked => unlockCycle == 2; 409 410 private List<List<long>> bankSectorSizes; // For one chip 411 private long[] sectorSizes; // For all chips in parallel 412 private long[] sectorStarts; // For all chips in parallel 413 private SectorRegion[] sectorRegions; // For one chip 414 private byte[] cfiData; 415 private byte[] autoselectData; 416 private State state; 417 private int unlockCycle; 418 419 private readonly MappedMemory underlyingMemory; 420 private readonly int interleave; 421 private readonly ushort manufacturerId; 422 private readonly ushort alternateManufacturerId; 423 private readonly uint deviceId; 424 private readonly Func<long, ushort> readMemoryWord; 425 private readonly Func<long, uint> readMemoryDoubleWord; 426 private readonly Action<long, ushort> writeMemoryWord; 427 private readonly Action<long, uint> writeMemoryDoubleWord; 428 private readonly Dictionary<long, BackingFile> backingFiles = new Dictionary<long, BackingFile>(); 429 430 private const byte ErasedValue = 0xFF; 431 432 private class SectorRegion 433 { SectorRegion(ulong size, ulong count)434 public SectorRegion(ulong size, ulong count) 435 { 436 this.size = size; 437 this.count = count; 438 } 439 440 public readonly ulong size; 441 public ulong count; 442 } 443 444 private class BackingFile 445 { BackingFile(int size, FilePath path)446 public BackingFile(int size, FilePath path) 447 { 448 this.size = size; 449 this.path = path; 450 } 451 452 public readonly int size; 453 public readonly FilePath path; 454 } 455 456 private enum Command : byte 457 { 458 Reset = 0xF0, 459 ReadArray = 0xFF, 460 ReadQuery = 0x98, 461 ReadAutoselect = 0x90, 462 UnlockCycle1 = 0xAA, 463 UnlockCycle2 = 0x55, 464 SectorEraseSetup = 0x80, 465 SectorErase = 0x30, 466 ProgramWord = 0xA0, 467 } 468 469 // Not currently used as the unlock mechanism is not address sensitive 470 private enum Registers : long 471 { 472 UnlockCycle1 = 0xAAA, 473 UnlockCycle2 = 0x555, 474 } 475 476 private enum State 477 { 478 ReadArray, 479 ReadQuery, 480 ReadAutoselect, 481 ProgramWord, 482 SectorEraseSetup, 483 } 484 } 485 } 486 487