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