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 System;
9 using System.IO;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Peripherals.Bus;
16 using Antmicro.Renode.Storage;
17 
18 namespace Antmicro.Renode.Peripherals.MTD
19 {
20     public static class SamsungK9NANDFlashExtensions
21     {
SamsungK9NANDFlashFromFile(this IMachine machine, string fileName, ulong busAddress, string name, bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null)22         public static void SamsungK9NANDFlashFromFile(this IMachine machine, string fileName, ulong busAddress, string name,
23             bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null)
24         {
25             SamsungK9NANDFlash flash;
26             try
27             {
28                 flash = new SamsungK9NANDFlash(machine, fileName, nonPersistent, partId, manufacturerId);
29             }
30             catch(Exception e)
31             {
32                 throw new ConstructionException($"Could not create {nameof(SamsungK9NANDFlash)}: {e.Message}", e);
33             }
34             machine.SystemBus.Register(flash, new BusRangeRegistration(busAddress, (ulong)flash.Size));
35             machine.SetLocalName(flash, name);
36         }
37     }
38 
39     public sealed class SamsungK9NANDFlash : BasicBytePeripheral, IKnownSize, IDisposable
40     {
SamsungK9NANDFlash(IMachine machine, string fileName, bool nonPersistent = false, byte? partId = null, byte? manufacturerId = null)41         public SamsungK9NANDFlash(IMachine machine, string fileName, bool nonPersistent = false,
42             byte? partId = null, byte? manufacturerId = null) : base(machine)
43         {
44             this.partId = partId ?? DefaultPartId;
45             this.manufacturerId = manufacturerId ?? DefaultManufacturerId;
46             backingStream = DataStorage.Create(fileName, persistent: !nonPersistent, paddingByte: ErasedValue);
47             addressBytes = new byte[AddressCycles];
48             DefineRegisters();
49             Reset();
50         }
51 
Reset()52         public override void Reset()
53         {
54             base.Reset();
55             state = State.ReadMemory;
56             idReadOffset = 0;
57             statusRegister = StatusFlags.WriteEnabled | StatusFlags.Ready;
58         }
59 
Dispose()60         public void Dispose()
61         {
62             backingStream.Dispose();
63         }
64 
65         // Note that this is not the size of the flash memory, but the size of its register window. This
66         // flash is not memory-mapped.
67         public long Size => 0x10;
68 
DefineRegisters()69         protected override void DefineRegisters()
70         {
71             Registers.Data.Define(this)
72                 .WithValueField(0, 8, valueProviderCallback: _ =>
73                 {
74                     switch(state)
75                     {
76                         case State.ReadMemory:
77                             return HandleReadByte();
78                         case State.ReadStatus:
79                             return (byte)statusRegister;
80                         case State.ReadID:
81                             return HandleReadID(idReadOffset++);
82                         case State.ReadOob:
83                             return ErasedValue;
84                         default:
85                             this.Log(LogLevel.Warning, "Data register read in unexpected state {0}", state);
86                             return 0;
87                     }
88                 }, writeCallback: (_, value) =>
89                 {
90                     switch(state)
91                     {
92                         case State.Programming:
93                             HandleProgramByte((byte)value);
94                             return;
95                         default:
96                             this.WarningLog("Data register written with 0x{0:x2} in unexpected state {1}", value, state);
97                             return;
98                     }
99                 });
100 
101             Registers.Address.Define(this)
102                 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) =>
103                 {
104                     if(addressCycle < AddressCycles)
105                     {
106                         addressBytes[addressCycle++] = (byte)value;
107                     }
108                     else
109                     {
110                         this.WarningLog("Too long ({0} bytes) address write: 0x{1:x2}", ++addressCycle, value);
111                         return;
112                     }
113 
114                     if(state == State.Erasing && addressCycle == EraseAddressCycles) // 3-byte address for erase
115                     {
116                         var memoryOffset = addressBytes[0] | (uint)addressBytes[1] << 8 | (uint)addressBytes[2] << 16;
117                         memoryOffset *= (uint)EraseBlockSize / 32; // The block number shifted into the device is multiplied by 32
118                         SeekStream(memoryOffset);
119                     }
120                     else if(addressCycle == AddressCycles) // 5-byte address for read/write
121                     {
122                         var column = addressBytes[0] | (uint)addressBytes[1] << 8;
123                         var row = addressBytes[2] | (uint)addressBytes[3] << 8 | (uint)addressBytes[4] << 16;
124                         var memoryOffset = row * RowWidth + column;
125                         SeekStream(memoryOffset);
126                     }
127                 });
128 
129             Registers.Command.Define(this)
130                 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) =>
131                 {
132                     // Get ready for a new address to be shifted in
133                     addressCycle = 0;
134 
135                     switch((Command)value)
136                     {
137                         case Command.ReadMemory1: // some flash chips might only have 1 command cycle for reading, this supports both
138                         case Command.ReadMemory2:
139                             state = State.ReadMemory;
140                             return;
141                         case Command.BlockErase1:
142                             state = State.Erasing;
143                             return;
144                         case Command.BlockErase2:
145                             HandleErase();
146                             state = State.ReadStatus;
147                             return;
148                         case Command.PageProgram1:
149                             state = State.Programming;
150                             return;
151                         case Command.ReadID:
152                             state = State.ReadID;
153                             return;
154                         case Command.ReadOob:
155                             state = State.ReadOob;
156                             return;
157                         case Command.PageProgram2: // do nothing, we already programmed on the #1 command
158                         case Command.ReadStatus:
159                             state = State.ReadStatus;
160                             return;
161                         case Command.Reset:
162                             Reset();
163                             return;
164                         default:
165                             this.WarningLog("Unknown command written, value 0x{0:x2}", value);
166                             return;
167                     }
168                 });
169         }
170 
HandleReadID(long offset)171         private byte HandleReadID(long offset)
172         {
173             switch(offset)
174             {
175                 case 0:
176                     return manufacturerId;
177                 case 1:
178                     return partId;
179                 default:
180                     this.Log(LogLevel.Warning, "ID Read at unsupported offset 0x{0:x} (tried to read extended ID?)", offset);
181                     return 0;
182             }
183         }
184 
SeekStream(long offset)185         private void SeekStream(long offset)
186         {
187             if(!IsValidOffset(offset))
188             {
189                 this.WarningLog("Tried to seek to invalid offset 0x{0:x}", offset);
190                 return;
191             }
192             backingStream.Seek(offset, SeekOrigin.Begin);
193         }
194 
HandleErase()195         private void HandleErase()
196         {
197             var offset = backingStream.Position;
198             if(!IsValidOffset(offset) || offset % EraseBlockSize != 0)
199             {
200                 this.ErrorLog("Invalid (or not divisible by 0x{0:x}) block erase address given: 0x{1:x}", EraseBlockSize, offset);
201                 statusRegister |= StatusFlags.ProgramEraseError;
202                 return;
203             }
204 
205             backingStream.Write(Enumerable.Repeat(ErasedValue, EraseBlockSize).ToArray(), 0, EraseBlockSize);
206             backingStream.Seek(offset, SeekOrigin.Begin);
207             this.NoisyLog("Erased block at offset 0x{0:x}, size 0x{1:x}", offset, EraseBlockSize);
208         }
209 
HandleReadByte()210         private byte HandleReadByte()
211         {
212             if(!IsValidOffset(backingStream.Position))
213             {
214                 return ErasedValue;
215             }
216             return (byte)backingStream.ReadByte();
217         }
218 
HandleProgramByte(byte value)219         private void HandleProgramByte(byte value)
220         {
221             var offset = backingStream.Position;
222             if(!IsValidOffset(offset))
223             {
224                 return;
225             }
226 
227             var flashCellState = (byte)(backingStream.ReadByte() & value);
228             backingStream.Seek(offset, SeekOrigin.Begin);
229             backingStream.WriteByte(flashCellState);
230             this.NoisyLog("Programmed byte at offset 0x{0:x} to 0x{1:x}{2}", offset, flashCellState,
231                 value == flashCellState ? "" : " using flash behavior");
232         }
233 
IsValidOffset(long offset)234         private bool IsValidOffset(long offset)
235         {
236             return offset >= 0 && offset < backingStream.Length;
237         }
238 
239         // The buffer size must be larger than the erase block size
240         private const int BufferSize = 100 * 1024;
241         private const int EraseBlockSize = 16 * 1024;
242         private const int DataBytesPerRow = 512;
243         private const int SpareBytesPerRow = 32;
244         private const int RowWidth = DataBytesPerRow + SpareBytesPerRow;
245         private const byte ErasedValue = 0xff;
246         private const byte DefaultManufacturerId = 0xec;
247         private const byte DefaultPartId = 0xd5;
248         // Address cycles for read and write
249         private const int AddressCycles = 5;
250         // Address cycles for block erase
251         private const int EraseAddressCycles = 3;
252         private readonly byte partId;
253         private readonly byte manufacturerId;
254         private readonly byte[] addressBytes;
255         private readonly Stream backingStream;
256 
257         private StatusFlags statusRegister;
258         private State state;
259         private uint idReadOffset;
260         private int addressCycle;
261 
262         private enum Command : byte
263         {
264             ReadID = 0x90,
265             ReadStatus = 0x70,
266             ReadOob = 0x50,
267             ReadMemory1 = 0x00,
268             ReadMemory2 = 0x30,
269             PageProgram1 = 0x80,
270             PageProgram2 = 0x10,
271             BlockErase1 = 0x60,
272             BlockErase2 = 0xD0,
273             Reset = 0xFF,
274         }
275 
276         [Flags]
277         private enum StatusFlags : byte
278         {
279             ProgramEraseError = 0x01,
280             Ready = 0x40,
281             WriteEnabled = 0x80,
282         }
283 
284         private enum State
285         {
286             ReadID,
287             ReadMemory,
288             ReadOob,
289             Erasing,
290             Programming,
291             ReadStatus
292         }
293 
294         private enum Registers
295         {
296             Data = 0x00,
297             Address = 0x04,
298             Command = 0x08,
299         }
300     }
301 }
302