1 #if !PLATFORM_WINDOWS 2 // 3 // Copyright (c) 2010-2024 Antmicro 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Text; 9 using System; 10 using System.IO; 11 using Mono.Unix; 12 using System.Net.Sockets; 13 using Antmicro.Renode.Sockets; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Core.Structure.Registers; 16 using Antmicro.Renode.Utilities; 17 using Antmicro.Renode.Core; 18 using Antmicro.Renode.Exceptions; 19 using Antmicro.Renode.Logging; 20 using Antmicro.Renode.Storage.VirtIO; 21 22 namespace Antmicro.Renode.Peripherals.Storage 23 { 24 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 25 public class VirtIOFSDevice : VirtIOMMIO, IDisposable 26 { VirtIOFSDevice(IMachine machine)27 public VirtIOFSDevice(IMachine machine) : base(machine) 28 { 29 BitHelper.SetBit(ref deviceFeatureBits, (byte)FeatureBits.FSFlagNotification, true); 30 } 31 Dispose()32 public void Dispose() 33 { 34 fsSocket?.Close(); 35 } 36 Create(string fsSocketPath, string tag = R, uint numRequestQueues = 1, uint queueSize = 128)37 public void Create(string fsSocketPath, string tag = "dummyfs", 38 uint numRequestQueues = 1, uint queueSize = 128) 39 { 40 if(String.IsNullOrEmpty(fsSocketPath)) 41 { 42 throw new RecoverableException("Missing filesystem socket path"); 43 } 44 if(String.IsNullOrEmpty(tag)) 45 { 46 throw new RecoverableException("Missing tag property"); 47 } 48 if(numRequestQueues == 0) 49 { 50 throw new RecoverableException("num-request-queues property must be larger than 0"); 51 } 52 if(!Misc.IsPowerOfTwo(queueSize)) 53 { 54 throw new RecoverableException("queue-size property must be a power of 2"); 55 } 56 if(queueSize > Virtqueue.QueueMaxSize) 57 { 58 throw new RecoverableException(String.Format("queue-size property must be {0} or smaller", Virtqueue.QueueMaxSize)); 59 } 60 61 this.Log(LogLevel.Debug, "Looking for UDS socket in path: {0}", Path.GetFullPath(fsSocketPath)); 62 fsSocket = SocketsManager.Instance.AcquireSocket(this, AddressFamily.Unix, SocketType.Stream, ProtocolType.IP, new UnixEndPoint(fsSocketPath), asClient: true); 63 64 StoreTag(tag, this.tag); 65 66 this.queueSize = queueSize; 67 this.numRequestQueues = numRequestQueues; 68 notifyBufSize = 2 * 255 + 2 * 48; //size of fuse_notify_fsnotify_out 69 // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#subsection.5.11.2 70 lastQueueIdx = numRequestQueues + 1; 71 Virtqueues = new Virtqueue[lastQueueIdx + 1]; 72 for (int i = 0; i <= lastQueueIdx; i++) 73 { 74 Virtqueues[i] = new Virtqueue(this, queueSize); 75 } 76 DefineRegisters(); 77 UpdateInterrupts(); 78 } 79 ProcessChain(Virtqueue vqueue)80 public override bool ProcessChain(Virtqueue vqueue) 81 { 82 if(fsSocket == null) 83 { 84 return false; 85 } 86 // Read request from buffers // 87 if(!vqueue.TryReadFromBuffers(FuseInHdrLen, out var fuseInHdr)) 88 { 89 this.Log(LogLevel.Error, "Error reading FUSE request header"); 90 return false; 91 } 92 93 this.Log(LogLevel.Debug, "FUSE request header: {0}", Misc.PrettyPrintCollection(fuseInHdr)); 94 fsSocket.Send(fuseInHdr); 95 96 var fuseInLen = BitConverter.ToInt32(fuseInHdr, 0); 97 if(!vqueue.TryReadFromBuffers(fuseInLen - FuseInHdrLen, out var fuseInData)) 98 { 99 this.Log(LogLevel.Error, "Error reading FUSE request data"); 100 return false; 101 } 102 this.Log(LogLevel.Debug, "FUSE request data: {0}", Misc.PrettyPrintCollection(fuseInData)); 103 fsSocket.Send(fuseInData); 104 105 // Read response from UDS // 106 var socketHdr = new byte[FuseOutHdrLen]; 107 fsSocket.Receive(socketHdr); 108 this.Log(LogLevel.Debug, "FUSE response header: {0}", Misc.PrettyPrintCollection(socketHdr)); 109 if(!vqueue.TryWriteToBuffers(socketHdr)) 110 { 111 this.Log(LogLevel.Error, "Error writing FUSE response header to buffer"); 112 return false; 113 } 114 115 var fuseOutLen = BitConverter.ToInt32(socketHdr, 0); 116 if(fuseOutLen > FuseOutHdrLen) 117 { 118 byte[] socketData = new byte[fuseOutLen-FuseOutHdrLen]; 119 fsSocket.Receive(socketData); 120 this.Log(LogLevel.Debug, "FUSE response data: {0}", Misc.PrettyPrintCollection(socketData)); 121 vqueue.TryWriteToBuffers(socketData); 122 } 123 return true; 124 } 125 126 protected override uint DeviceID => 26; 127 StoreTag(string tag, byte[] container)128 private void StoreTag(string tag, byte[] container) 129 { 130 tag = tag.Substring(0, Math.Min((int)MaxTagLen, tag.Length)); 131 byte[] toCopy = Encoding.ASCII.GetBytes(tag); 132 Array.Copy(toCopy, container, toCopy.Length); 133 } 134 DefineRegisters()135 private void DefineRegisters() 136 { 137 DefineMMIORegisters(); 138 Registers.Tag.DefineMany(this, 9, (reg, idx) => 139 { 140 reg.WithValueFields(0, 8, 4, FieldMode.Read, name: "tag", valueProviderCallback: (i, _) => tag[idx * 4 + i]); 141 }); 142 Registers.NumRequestQueues.Define(this) 143 .WithValueField(0, 32, FieldMode.Read, name: "num_virtqueues", valueProviderCallback: _ => numRequestQueues); 144 Registers.NotifyBufSize.Define(this) 145 .WithValueField(0, 32, FieldMode.Read, name: "notify_buf_size", valueProviderCallback: _ => notifyBufSize); 146 } 147 148 private byte[] tag = new byte[MaxTagLen]; 149 private uint queueSize; 150 private Socket fsSocket; 151 private uint numRequestQueues; 152 private uint notifyBufSize; 153 154 private const int FuseInHdrLen = 40; 155 private const int FuseOutHdrLen = 16; 156 private const uint MaxTagLen = 36; 157 158 private enum FeatureBits : byte 159 { 160 // File System device specific flags 161 // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#subsection.5.11.3 162 FSFlagNotification = 0, 163 } 164 165 private enum Registers : long 166 { 167 // Configuration space for file system device 168 // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#subsubsection.5.11.4.1 169 Tag = 0x100, 170 NumRequestQueues = 0x124, 171 NotifyBufSize = 0x128, 172 } 173 } 174 } 175 #endif 176