1 // 2 // Copyright (c) 2010-2023 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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals.Bus; 16 using Antmicro.Renode.Peripherals.PCI.BAR; 17 using Antmicro.Renode.Peripherals.PCI.Capabilities; 18 using Antmicro.Renode.Utilities; 19 20 using Range = Antmicro.Renode.Core.Range; 21 22 namespace Antmicro.Renode.Peripherals.PCI 23 { 24 public abstract class PCIeBasePeripheral : IPCIePeripheral 25 { PCIeBasePeripheral(IPCIeRouter parent, HeaderType headerType)26 protected PCIeBasePeripheral(IPCIeRouter parent, HeaderType headerType) 27 { 28 if(!IsHeaderAcceptable(headerType)) 29 { 30 throw new ConstructionException($"Currently only devices of type {HeaderType.Bridge} or {HeaderType.Endpoint} are supported."); 31 } 32 this.parent = parent; 33 this.HeaderType = headerType; 34 this.baseAddressRegisters = new BaseAddressRegister[headerType.MaxNumberOfBARs()]; 35 var registerMap = new Dictionary<long, DoubleWordRegister> 36 { 37 {(long)Registers.DeviceAndVendorId, new DoubleWordRegister(this) 38 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => VendorId) 39 .WithValueField(16, 16, FieldMode.Read, valueProviderCallback: _ => DeviceId) 40 }, 41 {(long)Registers.StatusAndConfiguration, new DoubleWordRegister(this) //unsupported fields do not have to be implemented. Maybe we should move it to inheriting classes? 42 //First 16 bits: command register. RW. Writing 0 to these fields should effectively disable all accesses but the configuration accesses 43 .WithTaggedFlag("I/O Space", 0) 44 .WithTaggedFlag("Memory Space", 1) 45 .WithTaggedFlag("Bus Master", 2) 46 .WithTaggedFlag("Special Cycles", 3) 47 .WithTaggedFlag("Memory Write and Invalidate Enable", 4) 48 .WithTaggedFlag("VGA Palette Snoop", 5) 49 .WithTaggedFlag("Parity Error Response", 6) 50 .WithReservedBits(7, 1) 51 .WithTaggedFlag("SERR# Enable", 8) 52 .WithTaggedFlag("Fast Back-to-Back Enable", 9) 53 .WithTaggedFlag("Interrupt Disable", 10) 54 .WithReservedBits(11, 8) 55 //Second 16 bits: status register. W1C. 56 .WithTaggedFlag("Interrupt Status", 19) 57 .WithFlag(20, FieldMode.Read, valueProviderCallback: _ => capabilities.Any(), name: "Capabilities List") 58 .WithTaggedFlag("66 MHz Capabale", 21) 59 .WithReservedBits(22, 1) 60 .WithTaggedFlag("Fast Back-to-Back capable", 23) 61 .WithTaggedFlag("Master Data Parity Error", 24) 62 .WithTag("DEVSEL Timing", 25, 2) 63 .WithTaggedFlag("Signaled Target Abort", 27) 64 .WithTaggedFlag("Received Target Abort", 28) 65 .WithTaggedFlag("Received Master Abort", 29) 66 .WithTaggedFlag("Signaled System Error", 30) 67 .WithTaggedFlag("Detected Parity Error", 31) 68 }, 69 {(long)Registers.ClassCode, new DoubleWordRegister(this) 70 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => RevisionId) 71 .WithValueField(16, 16, FieldMode.Read, valueProviderCallback: _ => ClassCode) 72 }, 73 {(long)Registers.Header, new DoubleWordRegister(this) 74 .WithTag("Cacheline Size", 0, 8) 75 .WithTag("Latency Timer", 8, 8) 76 .WithEnumField(16, 8, FieldMode.Read, valueProviderCallback: (HeaderType _) => headerType) 77 .WithTag("BIST", 24, 8) 78 }, 79 {(long)Registers.Capabilities, new DoubleWordRegister(this) 80 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => capabilities.FirstOrDefault().Key) 81 }, 82 }; 83 registers = new DoubleWordRegisterCollection(this, registerMap); 84 AddCapability(0x80, new PCIeCapability(this)); 85 } 86 ConfigurationReadDoubleWord(long offset)87 public virtual uint ConfigurationReadDoubleWord(long offset) 88 { 89 var value = registers.Read(offset); 90 this.Log(LogLevel.Noisy, "Accessing Configuration space, reading from {0} (0x{1:X}), read 0x{2:X}.", (Registers)offset, offset, value); 91 return value; 92 } 93 Reset()94 public virtual void Reset() 95 { 96 registers.Reset(); 97 } 98 ConfigurationWriteDoubleWord(long offset, uint value)99 public virtual void ConfigurationWriteDoubleWord(long offset, uint value) 100 { 101 this.Log(LogLevel.Noisy, "Accessing Configuration space, writing to {0} (0x{1:X}), value {2:X}.", (Registers)offset, offset, value); 102 registers.Write(offset, value); 103 } 104 MemoryReadDoubleWord(uint bar, long offset)105 public uint MemoryReadDoubleWord(uint bar, long offset) 106 { 107 if(bar >= baseAddressRegisters.Length || baseAddressRegisters[bar] == null) 108 { 109 this.Log(LogLevel.Warning, "Trying to read from unimplemented BAR {0}, offset 0x{1:X}.", bar, offset); 110 return 0; 111 } 112 if(baseAddressRegisters[bar].RequestedSize < offset) 113 { 114 this.Log(LogLevel.Error, "Trying to read outside the limits of BAR {0}, offset 0x{1:X}. The BAR requested 0x{2:X} bytes. This may indicate a problem in PCIe routing.", bar, offset, baseAddressRegisters[bar].RequestedSize); 115 return 0; 116 } 117 return ReadDoubleWordFromBar(bar, offset); 118 } 119 MemoryWriteDoubleWord(uint bar, long offset, uint value)120 public void MemoryWriteDoubleWord(uint bar, long offset, uint value) 121 { 122 if(bar >= baseAddressRegisters.Length || baseAddressRegisters[bar] == null) 123 { 124 this.Log(LogLevel.Warning, "Trying to write to unimplemented BAR {0}, offset 0x{1:X}, value 0x{2:X}.", bar, offset, value); 125 return; 126 } 127 if(baseAddressRegisters[bar].RequestedSize < offset) 128 { 129 this.Log(LogLevel.Error, "Trying to write outside the limits of BAR {0}, offset 0x{1:X}, value 0x{2:X}. The BAR requested 0x{3:X} bytes. This may indicate a problem in PCIe routing.", bar, offset, value, baseAddressRegisters[bar].RequestedSize); 130 return; 131 } 132 WriteDoubleWordToBar(bar, offset, value); 133 } 134 135 public ushort DeviceId { get; set; } 136 public ushort VendorId { get; set; } 137 public byte RevisionId { get; set; } 138 public uint ClassCode { get; set; } 139 public HeaderType HeaderType { get; } 140 WriteDoubleWordToBar(uint bar, long offset, uint value)141 protected virtual void WriteDoubleWordToBar(uint bar, long offset, uint value) 142 { 143 this.Log(LogLevel.Warning, "Unhandled write to BAR {0}, offset 0x{1:X}, value 0x{2:X}.", bar, offset, value); 144 } 145 ReadDoubleWordFromBar(uint bar, long offset)146 protected virtual uint ReadDoubleWordFromBar(uint bar, long offset) 147 { 148 this.Log(LogLevel.Warning, "Unhandled read from BAR {0}, offset 0x{1:X}.", bar, offset); 149 return 0; 150 } 151 AddBaseAddressRegister(uint i, BaseAddressRegister register)152 protected void AddBaseAddressRegister(uint i, BaseAddressRegister register) 153 { 154 if(i > HeaderType.MaxNumberOfBARs()) 155 { 156 throw new ConstructionException($"Cannot add Base address register {i} as it exceeds the maximum amount of these registers for this type of peripheral."); 157 } 158 159 if(baseAddressRegisters[i] != null) 160 { 161 throw new ConstructionException($"Base address register number at {i} is already registered."); 162 } 163 baseAddressRegisters[i] = register; 164 registers.AddRegister((long)Registers.BaseAddressRegister0 + 4 * i, new DoubleWordRegister(this) 165 .WithValueField(0, 32, changeCallback: (_, value) => baseAddressRegisters[i].Value = (uint)value, 166 valueProviderCallback: _ => baseAddressRegisters[i].Value, name: $"BAR{i}")) 167 .WithWriteCallback((_, value) => parent.RegisterBar(new Range(baseAddressRegisters[i].BaseAddress, baseAddressRegisters[i].RequestedSize), this, i)); 168 } 169 AddCapability(byte offset, Capability capability)170 protected void AddCapability(byte offset, Capability capability) 171 { 172 if(offset < CapabilitiesOffset) 173 { 174 throw new ConstructionException($"Capability offset (0x{offset:X}) is below the minimal address (0x{CapabilitiesOffset:X})"); 175 } 176 if(capabilities.ContainsKey(offset)) 177 { 178 throw new ConstructionException($"Capability already registered at 0x{offset}"); 179 } 180 if(capabilities.Any()) 181 { 182 var lastCapability = capabilities.Last(); 183 lastCapability.Value.NextCapability = offset; 184 } 185 capabilities.Add(offset, capability); 186 foreach(var register in capability.Registers) 187 { 188 registers.AddRegister(offset, register); 189 offset += 4; 190 } 191 } 192 193 protected readonly DoubleWordRegisterCollection registers; 194 IsHeaderAcceptable(HeaderType header)195 private bool IsHeaderAcceptable(HeaderType header) 196 { 197 var headerWithoutMultiFunctionFlag = header & ~HeaderType.MultiFunctionDevice; 198 //we do not check "HasFlag, because a) Endpoint == 0, b) they are mutually exclusive 199 return headerWithoutMultiFunctionFlag == HeaderType.Bridge || headerWithoutMultiFunctionFlag == HeaderType.Endpoint; 200 } 201 202 private readonly IPCIeRouter parent; 203 private readonly BaseAddressRegister[] baseAddressRegisters; 204 private readonly Dictionary<byte, Capability> capabilities = new Dictionary<byte, Capability>(); 205 206 private const byte CapabilitiesOffset = 0x40; 207 208 private enum Registers 209 { 210 DeviceAndVendorId = 0x0, 211 StatusAndConfiguration = 0x4, 212 ClassCode = 0x8, 213 Header = 0xc, 214 BaseAddressRegister0 = 0x10, 215 BaseAddressRegister1 = 0x14, 216 //missing offsets are type-specific 217 Capabilities = 0x34, 218 } 219 } 220 } 221 222