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