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