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