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.Exceptions;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Peripherals.CPU;
11 using Antmicro.Renode.Peripherals.IRQControllers.ARM_GenericInterruptControllerModel;
12 
13 namespace Antmicro.Renode.Peripherals.IRQControllers
14 {
15     public class ArmGicRedistributorRegistration : BusParametrizedRegistration
16     {
ArmGicRedistributorRegistration(IARMSingleSecurityStateCPU attachedCPU, ulong address, ICPU visibleTo = null, ICluster<ICPU> visibleToCluster = null)17         public ArmGicRedistributorRegistration(IARMSingleSecurityStateCPU attachedCPU, ulong address, ICPU visibleTo = null, ICluster<ICPU> visibleToCluster = null) : base(address, 0x20000, visibleTo, visibleToCluster)
18         {
19             Cpu = attachedCPU;
20         }
21 
GetWriteByteMethod(IBusPeripheral peripheral)22         public override Action<long, byte> GetWriteByteMethod(IBusPeripheral peripheral)
23         {
24             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
25             return (offset, value) =>
26             {
27                 gic.LockExecuteAndUpdate(() =>
28                     {
29                         var registerExists = IsByteAccessible(offset) && Utils.TryWriteByteToDoubleWordCollection(entry.RedistributorDoubleWordRegisters, offset, value, gic);
30                         gic.LogWriteAccess(registerExists, value, "Redistributor (byte access)", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset);
31                     }
32                 );
33             };
34         }
35 
GetReadByteMethod(IBusPeripheral peripheral)36         public override Func<long, byte> GetReadByteMethod(IBusPeripheral peripheral)
37         {
38             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
39             return (offset) =>
40             {
41                 byte value = 0;
42                 gic.LockExecuteAndUpdate(() =>
43                     {
44                         var registerExists = IsByteAccessible(offset) && Utils.TryReadByteFromDoubleWordCollection(entry.RedistributorDoubleWordRegisters, offset, out value, gic);
45                         gic.LogReadAccess(registerExists, value, "Redistributor (byte access)", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset);
46                     }
47                 );
48                 return value;
49             };
50         }
51 
GetWriteDoubleWordMethod(IBusPeripheral peripheral)52         public override Action<long, uint> GetWriteDoubleWordMethod(IBusPeripheral peripheral)
53         {
54             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
55             return (offset, value) =>
56             {
57                 gic.LockExecuteAndUpdate(() =>
58                     {
59                         // Only a few virtual quad word registers can't be double word accessed, assume all accesses are valid.
60                         var registerExists = entry.RedistributorDoubleWordRegisters.TryWrite(offset, value)
61                             || Utils.TryWriteDoubleWordToQuadWordCollection(entry.RedistributorQuadWordRegisters, offset, value, gic);
62                         gic.LogWriteAccess(registerExists, value, "Redistributor", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset);
63                     }
64                 );
65             };
66         }
67 
GetReadDoubleWordMethod(IBusPeripheral peripheral)68         public override Func<long, uint> GetReadDoubleWordMethod(IBusPeripheral peripheral)
69         {
70             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
71             return (offset) =>
72             {
73                 uint value = 0;
74                 gic.LockExecuteAndUpdate(() =>
75                     {
76                         // Only a few virtual quad word registers can't be double word accessed, assume all accesses are valid.
77                         var registerExists = entry.RedistributorDoubleWordRegisters.TryRead(offset, out value)
78                             || Utils.TryReadDoubleWordFromQuadWordCollection(entry.RedistributorQuadWordRegisters, offset, out value, gic);
79                         gic.LogReadAccess(registerExists, value, "Redistributor", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset);
80                     }
81                 );
82                 return value;
83             };
84         }
85 
GetWriteQuadWordMethod(IBusPeripheral peripheral)86         public override Action<long, ulong> GetWriteQuadWordMethod(IBusPeripheral peripheral)
87         {
88             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
89             return (offset, value) =>
90             {
91                 gic.LockExecuteAndUpdate(() =>
92                     gic.LogWriteAccess(entry.RedistributorQuadWordRegisters.TryWrite(offset, value), value, "Redistributor", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset)
93                 );
94             };
95         }
96 
GetReadQuadWordMethod(IBusPeripheral peripheral)97         public override Func<long, ulong> GetReadQuadWordMethod(IBusPeripheral peripheral)
98         {
99             GetGICAndCPUEntry(peripheral, out var gic, out var entry);
100             return (offset) =>
101             {
102                 ulong value = 0;
103                 gic.LockExecuteAndUpdate(() =>
104                     gic.LogReadAccess(entry.RedistributorQuadWordRegisters.TryRead(offset, out value), value, "Redistributor", offset, (ARM_GenericInterruptController.RedistributorRegisters)offset)
105                 );
106                 return value;
107             };
108         }
109 
RegisterForEachContext(Action<BusParametrizedRegistration> register)110         public override void RegisterForEachContext(Action<BusParametrizedRegistration> register)
111         {
112             RegisterForEachContextInner(register, visibleTo => new ArmGicRedistributorRegistration(Cpu, Range.StartAddress, visibleTo));
113         }
114 
GetGICAndCPUEntry(IBusPeripheral peripheral, out ARM_GenericInterruptController gic, out ARM_GenericInterruptController.CPUEntry entry)115         private void GetGICAndCPUEntry(IBusPeripheral peripheral, out ARM_GenericInterruptController gic, out ARM_GenericInterruptController.CPUEntry entry)
116         {
117             gic = peripheral as ARM_GenericInterruptController;
118             if(gic == null)
119             {
120                 throw new RegistrationException($"RedistributorRegistration can only be attached to {nameof(ARM_GenericInterruptController)}");
121             }
122             if(!gic.ArchitectureVersionAtLeast3)
123             {
124                 throw new RegistrationException($"RedistributorRegistration can only be attached to {nameof(ARM_GenericInterruptController)} version 3 and above");
125             }
126             if(!gic.TryGetCPUEntryForCPU(Cpu, out entry))
127             {
128                 throw new RegistrationException($"Couldn't register redistributor for CPU because the CPU isn't attached to this GIC: {Cpu.GetName()}");
129             }
130         }
131 
ToString()132         public override string ToString()
133         {
134             return $"{base.ToString()} GIC redistributor, attached CPU: {Cpu}";
135         }
136 
IsByteAccessible(long offset)137         private bool IsByteAccessible(long offset)
138         {
139             const long maxByteOffset = 3;
140             return (long)ARM_GenericInterruptController.RedistributorRegisters.InterruptPriority_0 <= offset && offset <= (long)ARM_GenericInterruptController.RedistributorRegisters.InterruptPriority_7 + maxByteOffset;
141         }
142 
143         public IARMSingleSecurityStateCPU Cpu { get; }
144     }
145 }
146