1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Storage; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Exceptions; 14 using System.IO; 15 using Antmicro.Renode.Core; 16 using Antmicro.Renode.UserInterface; 17 18 namespace Antmicro.Renode.Peripherals.MTD 19 { 20 [Icon("sd")] 21 public sealed class CFIFlash : IBytePeripheral, IWordPeripheral, IDoubleWordPeripheral, IKnownSize, IDisposable 22 { CFIFlash(string fileName, int? size = null, SysbusAccessWidth bits = SysbusAccessWidth.DoubleWord, bool nonPersistent = false)23 public CFIFlash(string fileName, int? size = null, SysbusAccessWidth bits = SysbusAccessWidth.DoubleWord, bool nonPersistent = false) 24 { 25 switch(bits) 26 { 27 case SysbusAccessWidth.Byte: 28 busWidth = 0; 29 break; 30 case SysbusAccessWidth.Word: 31 busWidth = 1; 32 break; 33 case SysbusAccessWidth.DoubleWord: 34 busWidth = 2; 35 break; 36 default: 37 throw new ArgumentOutOfRangeException(); 38 } 39 Init(fileName, size, nonPersistent); 40 CheckBuffer(0); 41 } 42 43 public long Size 44 { 45 get 46 { 47 return size; 48 } 49 } 50 51 /// <summary> 52 /// Gets or sets the size of the erase block. 53 /// </summary> 54 /// <value> 55 /// The size of the erase block in bytes. 56 /// </value> 57 /// <exception cref='ArgumentException'> 58 /// Thrown when erase block size is not divisible by 256 or greater than 16776960 or it 59 /// does not divide whole flash size. 60 /// </exception> 61 public int EraseBlockSize 62 { 63 get 64 { 65 return 256 * eraseBlockSizeDivided; 66 } 67 set 68 { 69 if(value % 256 != 0) 70 { 71 throw new ArgumentException("Erase block size has to be divisible by 256."); 72 } 73 if(size % value != 0) 74 { 75 throw new ArgumentException(string.Format( 76 "Erase block has to divide flash size, which is {0}B.", Misc.NormalizeBinary(size)) 77 ); 78 } 79 if(size / value > ushort.MaxValue + 1) 80 { 81 throw new ArgumentException(string.Format( 82 "Erase block cannot be smaller than {0}B for given flash size {1}B.", 83 Misc.NormalizeBinary(size / (ushort.MaxValue + 1)), 84 Misc.NormalizeBinary(Size)) 85 ); 86 } 87 if(value / 256 > ushort.MaxValue) 88 { 89 throw new ArgumentException(string.Format( 90 "Erase block cannot be larger than {0}B ({2}B was given) for given flash size {1}B.", 91 256 * ushort.MaxValue, 92 Misc.NormalizeBinary(Size), 93 value) 94 ); 95 } 96 eraseBlockSizeDivided = (ushort)(value / 256); 97 eraseBlockCountMinusOne = (ushort)(size / value - 1); 98 } 99 } 100 ReadByte(long offset)101 public byte ReadByte(long offset) 102 { 103 switch(state) 104 { 105 case State.ReadArray: 106 return (byte)HandleRead(offset, 0); 107 case State.ReadQuery: 108 return HandleQuery(offset); 109 case State.ActionDone: 110 return (byte)statusRegister; 111 case State.WaitingForElementCount: 112 return (byte)statusRegister; 113 case State.ReadJEDECId: 114 return HandleReadJEDEC(offset); 115 } 116 this.Log(LogLevel.Warning, string.Format( 117 "Read @ unknown state {1}, offset 0x{0:X}", offset, state) 118 ); 119 return 0; 120 } 121 ReadWord(long offset)122 public ushort ReadWord(long offset) 123 { 124 if(state == State.ReadArray) 125 { 126 return (ushort)HandleRead(offset, 1); 127 } 128 offset >>= (int)busWidth; 129 return ReadByte(offset); 130 } 131 ReadDoubleWord(long offset)132 public uint ReadDoubleWord(long offset) 133 { 134 if(state == State.ReadArray) 135 { 136 return HandleRead(offset, 2); 137 } 138 offset >>= (int)busWidth; 139 return ReadByte(offset); 140 } 141 WriteByte(long offset, byte value)142 public void WriteByte(long offset, byte value) 143 { 144 switch(state) 145 { 146 case State.ProgramWish: 147 HandleProgramByte(offset, value, 0); 148 return; 149 case State.WaitingForElementCount: 150 SetupWriteBuffer(offset, value); 151 return; 152 case State.WaitingForBufferData: 153 HandleMultiByteWrite(offset, value, 0); 154 return; 155 case State.WaitingForWriteConfirm: 156 HandleWriteConfirm(offset, value); 157 return; 158 } 159 switch(value) 160 { 161 case Command.ReadArray: 162 state = State.ReadArray; 163 return; 164 case Command.ReadQuery: 165 state = State.ReadQuery; 166 return; 167 case Command.EraseBlock: 168 state = State.EraseWish; 169 return; 170 case Command.ProgramByte: 171 state = State.ProgramWish; 172 return; 173 case Command.ReadJEDECId: // TODO 174 state = State.ReadJEDECId; 175 return; 176 case Command.ReadStatus: 177 state = State.ActionDone; 178 return; 179 case Command.SetupWriteBuffer: 180 state = State.WaitingForElementCount; 181 statusRegister |= StatusRegister.ActionDone; 182 return; 183 case Command.Confirm: 184 switch(state) 185 { 186 case State.EraseWish: 187 HandleErase(offset); 188 state = State.ActionDone; 189 return; 190 default: 191 this.Log(LogLevel.Warning, 192 "Confirm command sent in improper state {0}.", state); 193 break; 194 } 195 return; 196 case Command.ClearStatus: 197 statusRegister = 0; 198 this.NoisyLog("Status register cleared.", offset, EraseBlockSize); 199 return; 200 } 201 this.Log(LogLevel.Warning, 202 "Unknown command written @ offset 0x{0:X}, value 0x{1:X}.", offset, value); 203 } 204 WriteWord(long offset, ushort value)205 public void WriteWord(long offset, ushort value) 206 { 207 switch(state) 208 { 209 case State.ProgramWish: 210 HandleProgramByte(offset, value, 1); 211 break; 212 case State.WaitingForBufferData: 213 HandleMultiByteWrite(offset, value, 1); 214 break; 215 default: 216 WriteByte(offset, (byte)value); 217 break; 218 } 219 } 220 WriteDoubleWord(long offset, uint value)221 public void WriteDoubleWord(long offset, uint value) 222 { 223 switch(state) 224 { 225 case State.ProgramWish: 226 HandleProgramByte(offset, value, 2); 227 break; 228 case State.WaitingForBufferData: 229 HandleMultiByteWrite(offset, value, 2); 230 break; 231 default: 232 WriteByte(offset, (byte)value); 233 break; 234 } 235 } 236 Reset()237 public void Reset() 238 { 239 FlushBuffer(); 240 writeBuffer = null; 241 buffer = new byte[DesiredBufferSize]; 242 currentBufferSize = 0; 243 currentBufferStart = 0; 244 state = State.ReadArray; 245 CheckBuffer(0); 246 } 247 Dispose()248 public void Dispose() 249 { 250 this.NoisyLog("Dispose: flushing buffer and closing underlying stream."); 251 FlushBuffer(); 252 stream.Dispose(); 253 } 254 Init(string fileName, int? requestedSize, bool nonPersistent)255 private void Init(string fileName, int? requestedSize, bool nonPersistent) 256 { 257 if(nonPersistent) 258 { 259 var tempFile = TemporaryFilesManager.Instance.GetTemporaryFile(); 260 FileCopier.Copy(fileName, tempFile, true); 261 fileName = tempFile; 262 } 263 // if `requestedSize` is `null`, the file lenght will be used 264 stream = new SerializableStreamView(new FileStream(fileName, FileMode.OpenOrCreate), requestedSize, 0xFF); 265 size = (int)stream.Length; 266 CheckSize(size, requestedSize); 267 size2n = (byte)Misc.Logarithm2(size); 268 buffer = new byte[DesiredBufferSize]; 269 // default erase block is whole flash or 256KB 270 EraseBlockSize = Math.Min(size, DefaultEraseBlockSize); 271 } 272 HandleRead(long offset, int width)273 private uint HandleRead(long offset, int width) 274 { 275 CheckBuffer(offset); 276 var localOffset = offset - currentBufferStart; 277 var returnValue = (uint)buffer[localOffset]; 278 if(width > 0) 279 { 280 returnValue |= ((uint)buffer[localOffset + 1] << 8); 281 } 282 if(width > 1) 283 { 284 returnValue |= ((uint)buffer[localOffset + 2] << 16); 285 returnValue |= ((uint)buffer[localOffset + 3] << 24); 286 } 287 return returnValue; 288 } 289 HandleReadJEDEC(long offset)290 private byte HandleReadJEDEC(long offset) 291 { 292 switch(offset) 293 { 294 case 0: 295 return 0x89; 296 case 1: 297 return 0x18; 298 default: 299 this.Log(LogLevel.Warning, "Read @ unsupported offset 0x{0:X} while in state {1}.", offset, state); 300 return 0; 301 } 302 } 303 HandleQuery(long offset)304 private byte HandleQuery(long offset) 305 { 306 //TODO: enum/const!!! 307 this.NoisyLog("Query at 0x{0:X}.", offset); 308 switch(offset) 309 { 310 case 0x10: //Q 311 return 0x51; 312 case 0x11: //R 313 return 0x52; 314 case 0x12: //Y 315 return 0x59; 316 case 0x13: 317 return 0x03; // Intel command set 318 case 0x15: 319 return 0x31; 320 case 0x1B: 321 return 0x45; 322 case 0x1C: 323 return 0x55; 324 case 0x1F: 325 return 0x07; // timeout 326 case 0x20: 327 return 0x07; 328 case 0x21: 329 return 0x0A; 330 case 0x23: 331 return 0x04; // timeout 2 332 case 0x24: 333 return 0x04; 334 case 0x25: 335 return 0x04; 336 case 0x27: 337 return size2n; // size 338 case 0x28: 339 return 0x02; 340 case 0x2A: 341 return 8; // write buffer size 342 case 0x2C: 343 return 0x01; // no of erase block regions, currently one 344 case 0x2D: 345 return unchecked((byte)(eraseBlockCountMinusOne)); 346 case 0x2E: 347 return unchecked((byte)((eraseBlockCountMinusOne) >> 8)); 348 case 0x2F: 349 return unchecked((byte)eraseBlockSizeDivided); 350 case 0x30: 351 return unchecked((byte)(eraseBlockSizeDivided >> 8)); 352 case 0x31: 353 return 0x50; 354 case 0x32: 355 return 0x52; 356 case 0x33: 357 return 0x49; 358 case 0x34: 359 return 0x31; 360 case 0x35: 361 return 0x31; 362 default: 363 return 0x00; 364 } 365 } 366 HandleErase(long offset)367 private void HandleErase(long offset) 368 { 369 if(!IsBlockAddress(offset)) 370 { 371 this.Log(LogLevel.Error, 372 "Block address given to erase is not block address; given was 0x{0:X}. Cancelling erase.", offset); 373 statusRegister |= StatusRegister.EraseOrClearLockError; 374 return; 375 } 376 var inBuffer = false; 377 if(currentBufferStart <= offset && currentBufferStart + currentBufferSize >= offset + EraseBlockSize) 378 { 379 // we can handle erase in the current buffer 380 inBuffer = true; 381 for(var i = 0; i < EraseBlockSize; i++) 382 { 383 buffer[offset - currentBufferStart + i] = 0xFF; 384 } 385 } 386 else 387 { 388 FlushBuffer(); 389 DiscardBuffer(); 390 stream.Seek(offset, SeekOrigin.Begin); 391 var a = new byte[EraseBlockSize]; 392 for(var i = 0; i < a.Length; i++) 393 { 394 a[i] = 0xFF; 395 } 396 stream.Write(a, 0, EraseBlockSize); 397 CheckBuffer(offset); 398 } 399 statusRegister |= StatusRegister.ActionDone; 400 this.NoisyLog( 401 "Erased block @ offset 0x{0:X}, size 0x{1:X} ({2}).", offset, EraseBlockSize, inBuffer ? "in buffer" : "in stream"); 402 } 403 HandleProgramByte(long offset, uint value, int width)404 private void HandleProgramByte(long offset, uint value, int width) 405 { 406 CheckBuffer(offset); 407 dirty = true; 408 var index = offset - currentBufferStart; 409 WriteLikeFlash(ref buffer[index], (byte)value); 410 if(width > 0) 411 { 412 WriteLikeFlash(ref buffer[index + 1], (byte)(value >> 8)); 413 } 414 if(width > 1) 415 { 416 WriteLikeFlash(ref buffer[index + 2], (byte)(value >> 16)); 417 WriteLikeFlash(ref buffer[index + 3], (byte)(value >> 24)); 418 } 419 state = State.ActionDone; 420 statusRegister |= StatusRegister.ActionDone; 421 this.NoisyLog("Programmed byte @ offset 0x{0:X}, value 0x{1:X}.", offset, value); 422 } 423 HandleWriteConfirm(long offset, byte value)424 private void HandleWriteConfirm(long offset, byte value) 425 { 426 if(value != Command.Confirm) 427 { 428 // discarding data 429 this.NoisyLog("Discarded buffer of size {0}B.", Misc.NormalizeBinary(writeBuffer.Length)); 430 writeBuffer = null; 431 state = State.ReadArray; 432 return; 433 } 434 if(offset >= currentBufferStart && 435 writeBufferStart - currentBufferStart + writeBuffer.Length <= currentBufferSize) 436 { 437 writeBuffer.CopyTo(buffer, writeBufferStart - currentBufferStart); 438 dirty = true; 439 this.NoisyLog("Programmed buffer (with delayed write) of {0}B at 0x{1:X}.", 440 Misc.NormalizeBinary(writeBuffer.Length), writeBufferStart); 441 } 442 else 443 { 444 FlushBuffer(); 445 DiscardBuffer(); // to assure consistency 446 stream.Seek(writeBufferStart, SeekOrigin.Begin); 447 stream.Write(writeBuffer, 0, writeBuffer.Length); 448 this.NoisyLog("Programmed buffer (with direct write) of {0}B at 0x{1:X}.", 449 Misc.NormalizeBinary(writeBuffer.Length), writeBufferStart); 450 } 451 writeBuffer = null; 452 statusRegister |= StatusRegister.ActionDone; 453 state = State.ActionDone; 454 writtenCount = 0; 455 } 456 HandleMultiByteWrite(long offset, uint value, int width)457 private void HandleMultiByteWrite(long offset, uint value, int width) 458 { 459 if(writtenCount == 0) 460 { 461 writeBufferStart = offset; 462 if(likeFlashProgramming) 463 { 464 // maybe we can read from buffer 465 if(currentBufferStart <= offset && currentBufferStart + currentBufferSize >= writeBuffer.Length + offset) 466 { 467 for(var i = 0; i < writeBuffer.Length; i++) 468 { 469 writeBuffer[i] = buffer[offset - currentBufferStart + i]; 470 } 471 this.NoisyLog("Write buffer filled from buffer."); 472 } 473 else 474 { 475 FlushBuffer(); 476 stream.Seek(offset, SeekOrigin.Begin); 477 var read = stream.Read(writeBuffer, 0, writeBuffer.Length); 478 if(read != writeBuffer.Length) 479 { 480 this.Log(LogLevel.Error, 481 "Error while reading data to fill write buffer. Read {0}, but requested {1}.", 482 read, writeBuffer.Length); 483 } 484 this.NoisyLog("Write buffer filled from stream."); 485 } 486 } 487 } 488 #if DEBUG 489 if(offset != (writeBufferStart + writtenCount)) 490 { 491 this.Log(LogLevel.Error, 492 "Non continous write using write buffer, offset 0x{0:X} but buffer start 0x{1:X} and written count 0x{2:X}." + 493 "Possibility of data corruption.", offset, writeBufferStart, writtenCount); 494 } 495 #endif 496 WriteLikeFlash(ref writeBuffer[writtenCount], (byte)value); 497 writtenCount++; 498 if(width > 0) 499 { 500 WriteLikeFlash(ref writeBuffer[writtenCount], (byte)(value >> 8)); 501 writtenCount++; 502 } 503 if(width > 1) 504 { 505 WriteLikeFlash(ref writeBuffer[writtenCount], (byte)(value >> 16)); 506 WriteLikeFlash(ref writeBuffer[writtenCount + 1], (byte)(value >> 24)); 507 writtenCount += 2; 508 } 509 if(writtenCount == writeBuffer.Length) 510 { 511 state = State.WaitingForWriteConfirm; 512 } 513 } 514 SetupWriteBuffer(long offset, byte value)515 private void SetupWriteBuffer(long offset, byte value) 516 { 517 var size = ((value + 1) << (int)busWidth); 518 this.NoisyLog("Setting up write buffer of size {0}.", size); 519 writeBuffer = new byte[size]; 520 state = State.WaitingForBufferData; 521 writeBufferStart = offset; 522 } 523 IsBlockAddress(long offset)524 private bool IsBlockAddress(long offset) 525 { 526 return offset % EraseBlockSize == 0; 527 } 528 CheckSize(int sizeToCheck, int? requestedSize)529 private void CheckSize(int sizeToCheck, int? requestedSize) 530 { 531 if(sizeToCheck == 0 && !requestedSize.HasValue) 532 { 533 // most probably we've just created an empty file 534 throw new ConstructionException("Size must be provided when creating a new backend file"); 535 } 536 if(sizeToCheck < 256) 537 { 538 throw new ConstructionException("Size cannot be less than 256B."); 539 } 540 if((sizeToCheck & (sizeToCheck - 1)) != 0) 541 { 542 throw new ConstructionException("Size has to be power of two."); 543 } 544 } 545 CheckBuffer(long offset)546 private void CheckBuffer(long offset) 547 { 548 if(offset >= currentBufferStart && offset < currentBufferStart + currentBufferSize) 549 { 550 return; 551 } 552 FlushBuffer(); 553 var alignedAddress = offset & (~3); 554 this.NoisyLog("Reloading buffer at 0x{0:X}.", alignedAddress); 555 stream.Seek(alignedAddress, SeekOrigin.Begin); 556 currentBufferStart = alignedAddress; 557 currentBufferSize = unchecked((int)Math.Min(DesiredBufferSize, size - alignedAddress)); 558 var read = stream.Read(buffer, 0, currentBufferSize); 559 if(read != currentBufferSize) 560 { 561 this.Log(LogLevel.Error, "Error while reading from file: read {0}B, but {1}B requested.", 562 read, currentBufferSize); 563 } 564 } 565 FlushBuffer()566 private void FlushBuffer() 567 { 568 if(buffer == null || !dirty) 569 { 570 return; 571 } 572 this.NoisyLog("Buffer flushed."); 573 stream.Seek(currentBufferStart, SeekOrigin.Begin); 574 stream.Write(buffer, 0, currentBufferSize); 575 dirty = false; 576 } 577 DiscardBuffer()578 private void DiscardBuffer() 579 { 580 this.NoisyLog("Buffer discarded."); 581 currentBufferStart = 0; 582 currentBufferSize = 0; 583 dirty = false; 584 } 585 WriteLikeFlash(ref byte where, byte what)586 private void WriteLikeFlash(ref byte where, byte what) 587 { 588 if(likeFlashProgramming) 589 { 590 where &= what; 591 } 592 else 593 { 594 where = what; 595 } 596 } 597 598 private class Command 599 { 600 public const byte ReadArray = 0xFF; 601 public const byte ReadQuery = 0x98; 602 public const byte ReadJEDECId = 0x90; 603 public const byte ReadStatus = 0x70; 604 public const byte ClearStatus = 0x50; 605 public const byte EraseBlock = 0x20; 606 public const byte Confirm = 0xD0; 607 public const byte ProgramByte = 0x40; 608 public const byte PageLock = 0x60; 609 public const byte PageUnlock = 0x60; 610 public const byte SetupWriteBuffer = 0xE8; 611 } 612 613 private enum State : int 614 { 615 ReadArray = 0, 616 ReadStatus, 617 ReadQuery, 618 ReadJEDECId, 619 EraseWish, 620 ProgramWish, 621 WaitingForElementCount, 622 WaitingForBufferData, 623 WaitingForWriteConfirm, 624 ActionDone 625 } 626 627 [Flags] 628 private enum StatusRegister : byte 629 { 630 Protected = 2, 631 WriteOrSetLockError = 16, 632 EraseOrClearLockError = 32, 633 ActionDone = 128 634 } 635 636 private const int DesiredBufferSize = 100 * 1024; 637 private const int DefaultEraseBlockSize = 256 * 1024; 638 private const int CopyBufferSize = 256 * 1024; 639 private long currentBufferStart; 640 private int currentBufferSize; 641 private byte[] buffer; 642 private bool dirty; 643 private byte[] writeBuffer; 644 private long writeBufferStart; 645 private int writtenCount; 646 private ushort eraseBlockSizeDivided; 647 private ushort eraseBlockCountMinusOne; 648 private StatusRegister statusRegister; 649 private byte size2n; 650 private int size; 651 private State state; 652 private SerializableStreamView stream; 653 private readonly int busWidth; 654 private static readonly bool likeFlashProgramming = true; 655 } 656 } 657 658