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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Storage.VirtIO;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Peripherals.Storage
15 {
16     public abstract class VirtIOMMIO : VirtIO
17     {
VirtIOMMIO(IMachine machine)18         public VirtIOMMIO(IMachine machine) : base(machine)
19         {
20             BitHelper.SetBit(ref deviceFeatureBits, (byte)MMIOFeatureBits.Version1, true);
21             BitHelper.SetBit(ref deviceFeatureBits, (byte)MMIOFeatureBits.AccessPlatform, true);
22         }
23 
VirtqueueHandle()24         protected void VirtqueueHandle()
25         {
26             this.Log(LogLevel.Debug, "Handling virtqueue {0}", QueueSel);
27             var vqueue = Virtqueues[QueueSel];
28             vqueue.Handle();
29         }
30 
DefineMMIORegisters()31         protected void DefineMMIORegisters()
32         {
33             // General initialisation
34             MMIORegisters.MagicValue.Define(this, MagicNumber)
35                 .WithValueField(0, 32, FieldMode.Read, name: "magic_value");
36 
37             MMIORegisters.DeviceVersion.Define(this, Version)
38                 .WithValueField(0, 32, FieldMode.Read, name: "dev_version");
39 
40             MMIORegisters.DeviceID.Define(this)
41                 .WithValueField(0, 32, FieldMode.Read, name: "dev_id",
42                     valueProviderCallback: _ => DeviceID);
43 
44             MMIORegisters.VendorID.Define(this, VendorID)
45                 .WithValueField(0, 16, FieldMode.Read, name: "vendor_id")
46                 .WithReservedBits(16, 16);
47 
48             MMIORegisters.Status.Define(this)
49                 .WithFlag(0, out deviceStatusAcknowledge, name: "status_acknowledge")
50                 .WithFlag(1, out deviceStatusDriver, name: "status_driver")
51                 .WithFlag(2, out deviceStatusDriverOk, name: "status_driver_ok")
52                 .WithFlag(3, out deviceStatusFeaturesOk, name: "status_features_ok")
53                 .WithReservedBits(4, 2)
54                 .WithFlag(6, out deviceStatusNeedsReset, name: "status_device_needs_reset")
55                 .WithFlag(7, out deviceStatusFailed, name: "status_failed")
56                 .WithReservedBits(8, 24)
57                 .WithWriteCallback((_, val) => { if(val == 0) Reset(); });
58 
59             // Feature bits
60             // Reading from this register returns 32 consecutive flag bits, the least signifi-
61             // cant bit depending on the last value written to DeviceFeaturesSel. Access
62             // to this register returns bits DeviceFeaturesSel ∗ 32 to (DeviceFeaturesSel ∗
63             // 32)+31, e.g. feature bits from 0 to 31 if DeviceFeaturesSel is set to 0 and features
64             // bits 32 to 63 if DeviceFeaturesSel is set to 1
65             // Feature bits
66             MMIORegisters.DeviceFeatures.Define(this)
67                .WithValueField(0, 32, FieldMode.Read, name: "features", valueProviderCallback: _ =>
68                    deviceFeatureBitsIndex.Value ? deviceFeatureBits >> 32 : deviceFeatureBits);
69 
70             MMIORegisters.DeviceFeaturesSelected.Define(this)
71                 .WithFlag(0, out deviceFeatureBitsIndex, FieldMode.Write, name: "features_sel")
72                 .WithReservedBits(1, 31);
73 
74             MMIORegisters.DriverFeatures.Define(this)
75                .WithValueField(0, 32, FieldMode.Write, name: "guestbits", writeCallback: (_, val) =>
76                     driverFeatureBits = (driverFeatureBitsIndex.Value ? (ulong)val << 32 : 0));
77 
78             MMIORegisters.DriverFeaturesSelected.Define(this)
79                 .WithFlag(0, out driverFeatureBitsIndex, FieldMode.Write, name: "guest_sel")
80                 .WithReservedBits(1, 31);
81 
82             // Virtual queue index
83             // Writing to this register selects the virtual queue that the following op-
84             // erations on QueueNumMax, QueueNum, QueueReady, QueueDescLow,
85             // QueueDescHigh, QueueDriverlLow, QueueDriverHigh, QueueDeviceLow,
86             // QueueDeviceHigh and QueueReset apply to. The index number of the first
87             // queue is zero (0x0)
88             MMIORegisters.QueueSel.Define(this)
89                 .WithValueField(0, 32, FieldMode.Write, name: "queue_sel", writeCallback: (_, val) => QueueSel = (uint)val);
90 
91             MMIORegisters.QueueReady.Define(this)
92                 .WithFlag(0, name: "queue_ready",
93                     writeCallback: (_, val) =>
94                     {
95                         if(QueueSel > lastQueueIdx)
96                         {
97                             return;
98                         }
99                         Virtqueues[QueueSel].IsReady = val;
100                     },
101                     valueProviderCallback: _ =>
102                     {
103                         if(QueueSel > lastQueueIdx)
104                         {
105                             return false;
106                         }
107                         return Virtqueues[QueueSel].IsReady;
108                     }
109                 )
110                 .WithReservedBits(1, 31);
111 
112             MMIORegisters.QueueReset.Define(this)
113                 .WithFlag(0, name: "queue_reset",
114                     writeCallback: (_, val) => { if(val) Virtqueues[QueueSel].Reset(); },
115                     valueProviderCallback: _ => Virtqueues[QueueSel].IsReset
116                 )
117                 .WithReservedBits(1, 31);
118 
119             // Maximum virtual queue size
120             // Reading from the register returns the maximum size (number of elements)
121             // of the queue the device is ready to process or zero (0x0) if the queue is not
122             // available
123             // This applies to the queue selected by writing to QueueSel.
124             MMIORegisters.QueueNumMax.Define(this)
125                 .WithValueField(0, 32, FieldMode.Read, name: "queue_num_max",
126                     valueProviderCallback: _ =>
127                     {
128                         if(QueueSel > lastQueueIdx)
129                         {
130                             deviceStatusFailed.Value = true;
131                             return 0;
132                         }
133                         var vqueue = Virtqueues[QueueSel];
134                         return (ulong)vqueue.maxSize;
135                     });
136 
137             // Virtual queue size
138             // Queue size is the number of elements in the queue. Writing to this register
139             // notifies the device what size of the queue the driver will use. This applies
140             // to the queue selected by writing to QueueSel
141             MMIORegisters.QueueNum.Define(this)
142                 .WithValueField(0, 32, FieldMode.Write, name: "queue_num",
143                     writeCallback: (_, val) =>
144                     {
145                         Virtqueue vqueue = Virtqueues[QueueSel];
146                         if(val > vqueue.maxSize)
147                         {
148                             this.Log(LogLevel.Error, "Virtqueue size exceeded max available value!");
149                             deviceStatusFailed.Value = true;
150                         }
151                         Virtqueues[QueueSel].Size = val;
152                     });
153 
154             // Interrupts registers
155             MMIORegisters.InterruptStatus.Define(this)
156                 .WithFlag(0, FieldMode.Read, name: "has_used_buffer", valueProviderCallback: _ => hasUsedBuffer.Value)
157                 .WithFlag(1, FieldMode.Read, name: "config_has_changed", valueProviderCallback: _ => configHasChanged.Value)
158                 .WithReservedBits(2, 30);
159 
160             MMIORegisters.InterruptACK.Define(this)
161                 .WithFlag(0, out hasUsedBuffer, FieldMode.WriteOneToClear, name: "has_used_buffer")
162                 .WithFlag(1, out configHasChanged, FieldMode.WriteOneToClear, name: "config_has_changed")
163                 .WithWriteCallback((_, __) => UpdateInterrupts())
164                 .WithReservedBits(2, 30);
165 
166             MMIORegisters.QueueDescLow.Define(this)
167                 .WithValueField(0, 32, FieldMode.Write, name: "queue_desc_low", writeCallback: (_, val) =>
168                     Virtqueues[QueueSel].DescTableAddress = BitHelper.SetBitsFrom((ulong) val, Virtqueues[QueueSel].DescTableAddress, 31, 32));
169 
170             MMIORegisters.QueueDescHigh.Define(this)
171                 .WithValueField(0, 32, FieldMode.Write, name: "queue_desc_high", writeCallback: (_, val) =>
172                     Virtqueues[QueueSel].DescTableAddress = BitHelper.SetBitsFrom((ulong)val << 32, Virtqueues[QueueSel].DescTableAddress, 0, 32));
173 
174             MMIORegisters.QueueDriverLow.Define(this)
175                 .WithValueField(0, 32, FieldMode.Write, name: "queue_driver_low", writeCallback: (_, val) =>
176                     Virtqueues[QueueSel].AvailableAddress = BitHelper.SetBitsFrom((ulong)val, Virtqueues[QueueSel].AvailableAddress, 31, 32));
177 
178             MMIORegisters.QueueDriverHigh.Define(this)
179                 .WithValueField(0, 32, FieldMode.Write, name: "queue_driver_high", writeCallback: (_, val) =>
180                     Virtqueues[QueueSel].AvailableAddress = BitHelper.SetBitsFrom((ulong)val << 32, Virtqueues[QueueSel].AvailableAddress, 0, 32));
181 
182             MMIORegisters.QueueDeviceLow.Define(this)
183                 .WithValueField(0, 32, FieldMode.Write, name: "queue_device_low", writeCallback: (_, val) =>
184                     Virtqueues[QueueSel].UsedAddress = BitHelper.SetBitsFrom((ulong)val, Virtqueues[QueueSel].UsedAddress, 31, 32));
185 
186             MMIORegisters.QueueDeviceHigh.Define(this)
187                 .WithValueField(0, 32, FieldMode.Write, name: "queue_device_high", writeCallback: (_, val) =>
188                     Virtqueues[QueueSel].UsedAddress = BitHelper.SetBitsFrom((ulong)val << 32, Virtqueues[QueueSel].UsedAddress, 0, 32));
189 
190             MMIORegisters.QueueNotify.Define(this)
191                 .WithValueField(0, 32, FieldMode.Write, name: "queue_notifications",
192                     writeCallback: (_, val) =>
193                     {
194                         var idx = val & 0xFFFF;
195                         if(idx > lastQueueIdx)
196                         {
197                             this.Log(LogLevel.Error, "Tried to notify non-existent virtqueue");
198                             return;
199                         }
200                         if(!Virtqueues[idx].IsReady)
201                         {
202                             this.Log(LogLevel.Error, "VirtIO driver started an operation, but current virtqueue isn't marked as ready.");
203                         }
204                         else if(!deviceStatusDriverOk.Value)
205                         {
206                             this.Log(LogLevel.Error, "VirtIO driver started an operation, but DriverOK flag not set in status register.");
207                         }
208                         else
209                         {
210                             VirtqueueHandle();
211                         }
212                     });
213 
214             MMIORegisters.ConfigGeneration.Define(this)
215                 .WithValueField(0, 8, FieldMode.Read, name: "config_generation", valueProviderCallback: _ => 0x01)
216                 .WithReservedBits(8, 24);
217 
218             MMIORegisters.SHMSel.Define(this)
219                 .WithValueField(0, 32, FieldMode.Write, name: "shm_sel", writeCallback: (_, val) => sharedMemoryId = val);
220 
221             MMIORegisters.SHMLenLow.Define(this)
222                 .WithValueField(0, 32, FieldMode.Read, name: "shm_len_low", valueProviderCallback: _ =>
223                 {
224                     if(sharedMemoryId != 0)
225                     {
226                         return 0xFFFFFFFF;
227                     }
228                     return sharedMemoryLength;
229                 });
230 
231             MMIORegisters.SHMLenHigh.Define(this)
232                 .WithValueField(0, 32, FieldMode.Read, name: "shm_len_high", valueProviderCallback: _ =>
233                 {
234                     if(sharedMemoryId != 0)
235                     {
236                         return 0xFFFFFFFF;
237                     }
238                     return sharedMemoryLength >> 32;
239                 });
240 
241             MMIORegisters.SHMBaseLow.Define(this)
242                 .WithValueField(0, 32, FieldMode.Read, name: "shm_base_low", valueProviderCallback: _ =>
243                 {
244                     if(sharedMemoryId != 0)
245                     {
246                         return 0xFFFFFFFF;
247                     }
248                     return sharedMemoryBase;
249                 });
250 
251             MMIORegisters.SHMBaseHigh.Define(this)
252                 .WithValueField(0, 32, FieldMode.Read, name: "shm_base_high", valueProviderCallback: _ =>
253                 {
254                     if(sharedMemoryId != 0)
255                     {
256                         return 0xFFFFFFFF;
257                     }
258                     return sharedMemoryBase >> 32;
259                 });
260         }
261 
262         // Total number of request virtqueues exposed by the device
263         protected ulong sharedMemoryId;
264         protected ulong sharedMemoryLength;
265         protected ulong sharedMemoryBase;
266         protected int sharedMemoryFd;
267 
268         protected virtual uint DeviceID { get; }
269 
270         private const uint MagicNumber = 0x74726976;
271         private const uint Version = 0x2;
272         private const uint VendorID = 0x1AF4; // Constant value taken from https://wiki.osdev.org/Virtio
273 
274         [Flags]
275         protected enum MMIOFeatureBits : byte
276         {
277             // VirtIO MMIO device specific flags
278             // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#chapter.6
279             RingIndirectDescriptors = 28,
280             RingEventIndex = 29,
281             Version1 = 32,
282             AccessPlatform = 33,
283             RingPacket = 34,
284             InOrder = 35,
285             OrderPlatform = 36,
286             SingleRootIOVirtualization = 37,
287             NotificationData = 38,
288             NotificationConfigData = 39,
289         }
290 
291         private enum MMIORegisters : long
292         {
293             // Configuration space for MMIO device
294             // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.pdf#subsubsection.4.2.2.1
295             MagicValue = 0x00,
296             DeviceVersion = 0x04,
297             DeviceID = 0x08,
298             VendorID = 0x0c,
299             DeviceFeatures = 0x10,
300             DeviceFeaturesSelected = 0x14,
301             DriverFeatures = 0x20,
302             DriverFeaturesSelected = 0x24,
303             QueueSel = 0x30,
304             QueueNumMax = 0x34,
305             QueueNum = 0x38,
306             QueueReady = 0x44,
307             QueueNotify = 0x50,
308             InterruptStatus = 0x60,
309             InterruptACK = 0x64,
310             Status = 0x70,
311             QueueDescLow = 0x80,
312             QueueDescHigh = 0x84,
313             QueueDriverLow = 0x90,
314             QueueDriverHigh = 0x94,
315             QueueDeviceLow = 0xa0,
316             QueueDeviceHigh = 0xa4,
317             SHMSel = 0xac,
318             SHMLenLow = 0xb0,
319             SHMLenHigh = 0xb4,
320             SHMBaseLow = 0xb8,
321             SHMBaseHigh = 0xbc,
322             QueueReset = 0xc0,
323             ConfigGeneration = 0xfc,
324         }
325     }
326 }
327