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 using System; 8 using System.IO; 9 using System.Runtime.InteropServices; 10 using Antmicro.Renode.Peripherals.Bus; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Storage; 15 using Antmicro.Renode.Storage.VirtIO; 16 using Antmicro.Renode.Exceptions; 17 using Antmicro.Renode.Utilities; 18 using Antmicro.Renode.Utilities.Packets; 19 20 namespace Antmicro.Renode.Peripherals.Storage 21 { 22 // VirtIO class implementing VirtIO block devices. 23 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 24 public class VirtIOBlockDevice : VirtIOMMIO, IDisposable 25 { VirtIOBlockDevice(IMachine machine)26 public VirtIOBlockDevice(IMachine machine) : base(machine) 27 { 28 storage = DataStorage.Create(size: 0); 29 lastQueueIdx = 0; 30 Virtqueues = new Virtqueue[lastQueueIdx + 1]; 31 for (int i = 0; i <= lastQueueIdx; i++) 32 { 33 Virtqueues[i] = new Virtqueue(this, Virtqueue.QueueMaxSize); 34 } 35 BitHelper.SetBit(ref deviceFeatureBits, (byte)FeatureBits.BlockFlagFlush, true); 36 BitHelper.SetBit(ref deviceFeatureBits, (byte)FeatureBits.BlockFlagConfigWCE, true); 37 DefineRegisters(); 38 } 39 Dispose()40 public void Dispose() 41 { 42 storage?.Dispose(); 43 } 44 LoadImage(WriteFilePath file, bool persistent = false)45 public void LoadImage(WriteFilePath file, bool persistent = false) 46 { 47 storage?.Dispose(); 48 storage = DataStorage.Create(file, persistent: persistent); 49 capacity = (long)Math.Ceiling((decimal)storage.Length / SectorSize); 50 configHasChanged.Value = true; 51 UpdateInterrupts(); 52 } 53 WriteStatus(Virtqueue vqueue)54 public void WriteStatus(Virtqueue vqueue) 55 { 56 vqueue.ReadDescriptorMetadata(); 57 SystemBus.WriteByte(vqueue.Descriptor.BufferAddress, status); 58 } 59 Flush()60 public void Flush() 61 { 62 storage.Flush(); 63 } 64 MarkAsUnsupported()65 public void MarkAsUnsupported() 66 { 67 status = (byte)VirtIOBlockRequestStatus.Unsupported; 68 this.Log(LogLevel.Warning, "Block operation unsupported."); 69 } 70 ProcessChain(Virtqueue vqueue)71 public override bool ProcessChain(Virtqueue vqueue) 72 { 73 vqueue.ReadDescriptorMetadata(); 74 vqueue.TryReadFromBuffers(Marshal.SizeOf(typeof(Header)), out var hdrBuff); 75 if(!Packet.TryDecode<Header>(hdrBuff, out var hdr)) 76 { 77 this.Log(LogLevel.Error, "Error decoding block request header"); 78 return false; 79 } 80 SeekToSector(hdr.sector); 81 vqueue.ReadDescriptorMetadata(); 82 var length = vqueue.Descriptor.Length; 83 84 switch(hdr.type) 85 { 86 case BlockOperations.Out: 87 if(!vqueue.TryReadFromBuffers(length, out var res)) 88 { 89 return false; 90 } 91 storage.Write(res, 0, length); 92 break; 93 94 case BlockOperations.In: 95 byte[] driverBytes = new byte[length]; 96 storage.Read(driverBytes, 0, length); 97 if(!vqueue.TryWriteToBuffers(driverBytes)) 98 { 99 return false; 100 } 101 break; 102 103 case BlockOperations.Flush: 104 if(IsFeatureEnabled((byte)FeatureBits.BlockFlagFlush)) 105 { 106 Flush(); 107 } 108 else 109 { 110 MarkAsUnsupported(); 111 } 112 break; 113 114 default: 115 this.Log(LogLevel.Error, "Unsupported block operation ({0})", hdr.type); 116 break; 117 } 118 119 WriteStatus(vqueue); 120 return true; 121 } 122 123 protected override uint DeviceID => 0x2; 124 DefineRegisters()125 private void DefineRegisters() 126 { 127 DefineMMIORegisters(); 128 Registers.CapacityHigh.Define(this) 129 .WithValueField(0, 32, FieldMode.Read, name: "capacity_high", valueProviderCallback: _ => (uint)(capacity >> 32)); 130 131 Registers.CapacityLow.Define(this) 132 .WithValueField(0, 32, FieldMode.Read, name: "capacity_low", valueProviderCallback: _ => (uint)capacity); 133 134 // With this register driver can choose whether it will use write-back or write-through caching mode. 135 // It should be 0 by default. 136 Registers.Writeback.Define(this) 137 .WithValueField(0, 8, FieldMode.Read, name: "writeback", valueProviderCallback: _ => 0); 138 } 139 SeekToSector(long sector)140 private void SeekToSector(long sector) 141 { 142 var positionToSeek = SectorSize * sector; 143 if(positionToSeek >= this.storage.Length) 144 { 145 throw new RecoverableException("Driver tried to seek beyond the loaded image end."); 146 } 147 storage.Seek(positionToSeek, SeekOrigin.Begin); 148 } 149 150 private long capacity; 151 private Stream storage; 152 private byte status; 153 154 private const int SectorSize = 0x200; 155 156 private enum FeatureBits : byte 157 { 158 // Block device specific flags 159 BlockFlagSizeMax = 1, 160 BlockFlagSegmentsMaxNum = 2, 161 BlockFlagGeometry = 4, 162 BlockFlagReadOnly = 5, 163 BlockFlagBlockSize = 6, 164 BlockFlagFlush = 9, 165 BlockFlagTopology = 10, 166 BlockFlagConfigWCE = 11, 167 BlockFlagDiscard = 13, 168 BlockFlagWriteZeroes = 14, 169 } 170 171 private enum BlockRequestHeader 172 { 173 Type = 0x0, 174 SectorLow = 0x8, 175 SectorHigh = 0xc, 176 } 177 178 private enum BlockOperations : int 179 { 180 In = 0, 181 Out = 1, 182 Flush = 4, 183 Discard = 11, 184 WriteZeroes = 13, 185 } 186 187 private enum VirtIOBlockRequestStatus : byte 188 { 189 Success = 0, 190 IoError = 1, 191 Unsupported = 2, 192 } 193 194 private enum Registers : long 195 { 196 // Configuration space for block device 197 // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#subsection.5.2.4 198 CapacityLow = 0x100, 199 CapacityHigh = 0x104, 200 SizeMax = 0x108, 201 SegMax = 0x10c, 202 Geometry = 0x110, 203 BlockSize = 0x114, 204 TopologyHigh = 0x118, 205 TopologyLow = 0x11c, 206 Writeback = 0x120, 207 MaxDiscardSectors = 0x124, 208 MaxDiscardSeg = 0x128, 209 DiscardSectorAlignment = 0x12c, 210 MaxWriteZeroesSectors = 0x130, 211 MaxWriteZeroesSeg = 0x134, 212 WriteZeroesMayUnmap = 0x138, 213 } 214 215 [LeastSignificantByteFirst] 216 private struct Header 217 { 218 #pragma warning disable 0649 219 [PacketField, Width(32)] 220 public BlockOperations type; 221 [PacketField, Offset(doubleWords: 2), Width(64)] 222 public long sector; 223 #pragma warning restore 0649 224 // we don't use other fields from the documentation 225 } 226 } 227 } 228