1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.IO;
9 using Antmicro.Renode.Exceptions;
10 using Antmicro.Renode.Peripherals;
11 using Antmicro.Renode.Peripherals.CPU;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Core.Extensions
15 {
16     public static class MemoryDumpExtensions
17     {
DumpBinary(this IMemory memory, SequencedFilePath fileName, ulong offset = 0, ICPU context = null)18         public static void DumpBinary(this IMemory memory, SequencedFilePath fileName, ulong offset = 0, ICPU context = null)
19         {
20             memory.DumpBinary(fileName, offset, (ulong)memory.Size - offset, context);
21         }
22 
DumpBinary(this IMemory memory, SequencedFilePath fileName, ulong offset, ulong size, ICPU context = null)23         public static void DumpBinary(this IMemory memory, SequencedFilePath fileName, ulong offset, ulong size, ICPU context = null)
24         {
25             AssertArguments(memory, offset, size);
26 
27             try
28             {
29                 using(var writer = new FileStream(fileName, FileMode.Create, FileAccess.Write))
30                 {
31                     var windows = (int)((size - offset + WindowSize - 1) / WindowSize - 1);
32                     for(var i = 0; i < windows; ++i)
33                     {
34                         WriteBinaryMemoryChunk(writer, memory, offset, i, context);
35                     }
36 
37                     var lastChunkSize = (size - offset) % WindowSize;
38                     lastChunkSize = lastChunkSize == 0 ? WindowSize : lastChunkSize;
39                     WriteBinaryMemoryChunk(writer, memory, offset, windows, context, lastChunkSize);
40                 }
41             }
42             catch(IOException e)
43             {
44                 throw new RecoverableException($"Exception while saving to file {fileName}: {e.Message}");
45             }
46         }
47 
DumpHEX(this IMemory memory, SequencedFilePath fileName, ulong offset = 0, ICPU context = null, HexAddressOption addressOption = HexAddressOption.OffsetRelativeAddress)48         public static void DumpHEX(this IMemory memory, SequencedFilePath fileName, ulong offset = 0, ICPU context = null, HexAddressOption addressOption = HexAddressOption.OffsetRelativeAddress)
49         {
50             memory.DumpHEX(fileName, offset, (ulong)memory.Size - offset, context, addressOption);
51         }
52 
DumpHEX(this IMemory memory, SequencedFilePath fileName, ulong offset, ulong size, ICPU context = null, HexAddressOption addressOption = HexAddressOption.OffsetRelativeAddress)53         public static void DumpHEX(this IMemory memory, SequencedFilePath fileName, ulong offset, ulong size, ICPU context = null, HexAddressOption addressOption = HexAddressOption.OffsetRelativeAddress)
54         {
55             AssertArguments(memory, offset, size);
56             var extendedAddress = 0UL;
57             var baseAddress = 0UL;
58             switch(addressOption)
59             {
60             case HexAddressOption.OffsetRelativeAddress:
61                 break;
62             case HexAddressOption.PeripheralRelativeAddress:
63                 baseAddress = offset;
64                 break;
65             default:
66                 throw new RecoverableException($"Invalid '{nameof(addressOption)}' value");
67             }
68 
69             if(baseAddress + size > (1UL << 32))
70             {
71                 throw new RecoverableException("Hex format is limited to 4GiB addressing");
72             }
73 
74             try
75             {
76                 using(var writer = new StreamWriter(fileName))
77                 {
78                     var chunks = (int)((size - offset + MaxHexRecordDataLength - 1) / MaxHexRecordDataLength - 1);
79                     for(var i = 0; i < chunks; ++i)
80                     {
81                         WriteHexMemoryChunk(writer, memory, offset, baseAddress, ref extendedAddress, i, context);
82                     }
83 
84                     var lastChunkSize = (size - offset) % MaxHexRecordDataLength;
85                     lastChunkSize = lastChunkSize == 0 ? MaxHexRecordDataLength : lastChunkSize;
86                     WriteHexMemoryChunk(writer, memory, offset, baseAddress, ref extendedAddress, chunks, context, lastChunkSize);
87                     writer.WriteLine(":00000001FF"); // End Of File record
88                 }
89             }
90             catch(IOException e)
91             {
92                 throw new RecoverableException($"Exception while saving to file {fileName}: {e.Message}");
93             }
94         }
95 
WriteBinaryMemoryChunk(FileStream writer, IMemory memory, ulong offset, int chunk, ICPU context, ulong size = WindowSize)96         private static void WriteBinaryMemoryChunk(FileStream writer, IMemory memory, ulong offset, int chunk, ICPU context, ulong size = WindowSize)
97         {
98             var data = memory.ReadBytes((long)(offset + (ulong)chunk * WindowSize), (int)size, context);
99             writer.Write(data, offset: 0, count: (int)size);
100         }
101 
WriteHexMemoryChunk(StreamWriter writer, IMemory memory, ulong offset, ulong baseAddress, ref ulong extendedAddress, int chunk, ICPU context, ulong size = MaxHexRecordDataLength)102         private static void WriteHexMemoryChunk(StreamWriter writer, IMemory memory, ulong offset, ulong baseAddress, ref ulong extendedAddress, int chunk, ICPU context, ulong size = MaxHexRecordDataLength)
103         {
104             var readOffset = offset + (ulong)chunk * MaxHexRecordDataLength;
105             var address = baseAddress + readOffset - extendedAddress;
106             var data = memory.ReadBytes((long)readOffset, (int)size, context);
107 
108             if(address > UInt16.MaxValue)
109             {
110                 var extendedBy = address & ~(ulong)UInt16.MaxValue;
111                 extendedAddress += extendedBy;
112                 address -= extendedBy;
113                 writer.WriteHexExtendedLinearAddressRecord((uint)extendedAddress);
114             }
115 
116             writer.WriteHexDataRecord((byte)size, (ushort)address, data);
117         }
118 
WriteHexExtendedLinearAddressRecord(this StreamWriter writer, uint extendedAddress)119         private static void WriteHexExtendedLinearAddressRecord(this StreamWriter writer, uint extendedAddress)
120         {
121             byte length = 2;
122             ushort address = 0;
123             byte type = 4;
124             extendedAddress = extendedAddress >> 16;
125             var checksum = GetHexChecksum(length, address, type, BitHelper.GetBytesFromValue(extendedAddress, typeSize: 2));
126             writer.WriteLine($":{length:X02}{address:X04}{type:X02}{extendedAddress:X04}{checksum:X02}");
127         }
128 
WriteHexDataRecord(this StreamWriter writer, byte length, ushort address, byte[] data)129         private static void WriteHexDataRecord(this StreamWriter writer, byte length, ushort address, byte[] data)
130         {
131             byte type = 0;
132             writer.Write($":{length:X02}{address:X04}{type:X02}");
133             writer.Write(data.ToHexString());
134             writer.WriteLine("{0:X02}", GetHexChecksum(length, address, type, data));
135         }
136 
GetHexChecksum(byte length, ushort address, byte type, byte[] data)137         private static byte GetHexChecksum(byte length, ushort address, byte type, byte[] data)
138         {
139             var checksum = length + address + (address >> 8) + type;
140             foreach(var b in data)
141             {
142                 checksum += b;
143             }
144             return (byte)(-checksum);
145         }
146 
AssertArguments(this IMemory memory, ulong offset, ulong size)147         private static void AssertArguments(this IMemory memory, ulong offset, ulong size)
148         {
149             if(size == 0)
150             {
151                 throw new RecoverableException($"'{nameof(size)}' must be greater than zero");
152             }
153             if(offset > (ulong)memory.Size)
154             {
155                 throw new RecoverableException($"'{nameof(offset)}' is outside of memory");
156             }
157             if(offset + size > (ulong)memory.Size)
158             {
159                 throw new RecoverableException($"'{nameof(size)}' is too big, region is outside of memory");
160             }
161         }
162 
163         private const ulong WindowSize = 100 * 1024;
164         private const ulong MaxHexRecordDataLength = Byte.MaxValue;
165 
166         public enum HexAddressOption
167         {
168             OffsetRelativeAddress,
169             PeripheralRelativeAddress,
170         }
171     }
172 }
173