1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using Antmicro.Renode.Core; 8 using Antmicro.Renode.Utilities.Binding; 9 using System.Collections.Generic; 10 using System; 11 using Antmicro.Renode.Time; 12 using ELFSharp.ELF; 13 using Machine = Antmicro.Renode.Core.Machine; 14 using ELFSharp.ELF.Sections; 15 using System.Linq; 16 using Antmicro.Renode.Logging; 17 using ELFSharp.UImage; 18 19 namespace Antmicro.Renode.Peripherals.CPU 20 { 21 // This model is a retro-fitted PowerPc. Both should be unified, as they are almost identical. 22 [GPIO(NumberOfInputs = 1)] 23 public partial class PowerPc64 : TranslationCPU, ICPUWithHooks 24 { 25 // Note that the reported endianness will be wrong if it is switched at runtime! PowerPc64(IMachine machine, string cpuType = R, Endianess endianness = Endianess.BigEndian)26 public PowerPc64(IMachine machine, string cpuType = "620", Endianess endianness = Endianess.BigEndian) : base(cpuType, machine, 27 endianness, CpuBitness.Bits64) 28 { 29 initialEndianess = endianness; 30 irqSync = new object(); 31 machine.ClockSource.AddClockEntry( 32 new ClockEntry(long.MaxValue / 2, 128000000, DecrementerHandler, this, String.Empty, false, Direction.Descending)); 33 TlibSetLittleEndianMode(initialEndianess == Endianess.LittleEndian ? 1u : 0u); 34 } 35 Reset()36 public override void Reset() 37 { 38 base.Reset(); 39 TlibSetLittleEndianMode(initialEndianess == Endianess.LittleEndian ? 1u : 0u); 40 } 41 InitFromUImage(UImage uImage)42 public override void InitFromUImage(UImage uImage) 43 { 44 this.Log(LogLevel.Warning, "PowerPC64 VLE mode not implemented for uImage loading."); 45 base.InitFromUImage(uImage); 46 } 47 InitFromElf(IELF elf)48 public override void InitFromElf(IELF elf) 49 { 50 base.InitFromElf(elf); 51 52 var bamSection = elf.GetSections<Section<uint>>().FirstOrDefault(x => x.Name == ".__bam_bootarea"); 53 if(bamSection != null) 54 { 55 var bamSectionContents = bamSection.GetContents(); 56 var isValidResetConfigHalfWord = bamSectionContents[1] == 0x5a; 57 if(!isValidResetConfigHalfWord) 58 { 59 this.Log(LogLevel.Warning, "Invalid BAM section, ignoring."); 60 } 61 else 62 { 63 StartInVle = (bamSectionContents[0] & 0x1) == 1; 64 this.Log(LogLevel.Info, "Will {0}start in VLE mode.", StartInVle ? "" : "not "); 65 } 66 } 67 } 68 OnGPIO(int number, bool value)69 public override void OnGPIO(int number, bool value) 70 { 71 InternalSetInterrupt(InterruptType.External, value); 72 } 73 74 public override string Architecture { get { return "ppc64"; } } 75 76 public override string GDBArchitecture { get { return "powerpc:common64"; } } 77 78 public override List<GDBFeatureDescriptor> GDBFeatures 79 { 80 get 81 { 82 var powerCore = new GDBFeatureDescriptor("org.gnu.gdb.power.core"); 83 for(var index = 0u; index < 32; index++) 84 { 85 powerCore.Registers.Add(new GDBRegisterDescriptor(index, 64, $"r{index}", "uint64", "general")); 86 } 87 88 powerCore.Registers.Add(new GDBRegisterDescriptor(64, 64, "pc", "code_ptr", "general")); 89 powerCore.Registers.Add(new GDBRegisterDescriptor(65, 64, "msr", "uint64", "general")); 90 powerCore.Registers.Add(new GDBRegisterDescriptor(66, 32, "cr", "uint32", "general")); 91 powerCore.Registers.Add(new GDBRegisterDescriptor(67, 64, "lr", "code_ptr", "general")); 92 powerCore.Registers.Add(new GDBRegisterDescriptor(68, 64, "ctr", "uint64", "general")); 93 powerCore.Registers.Add(new GDBRegisterDescriptor(69, 32, "xer", "uint32", "general")); 94 95 return new List<GDBFeatureDescriptor>(new GDBFeatureDescriptor[] { powerCore }); 96 } 97 } 98 99 public bool WaitAsNop 100 { 101 get => neverWaitForInterrupt; 102 set 103 { 104 neverWaitForInterrupt = value; 105 } 106 } 107 DecodeInterrupt(int number)108 protected override Interrupt DecodeInterrupt(int number) 109 { 110 if(number == 0) 111 { 112 return Interrupt.Hard; 113 } 114 throw InvalidInterruptNumberException; 115 } 116 117 [Export] ReadTbl()118 public uint ReadTbl() 119 { 120 tb += 0x100; 121 return tb; 122 } 123 124 [Export] ReadTbu()125 public uint ReadTbu() 126 { 127 return 0; 128 } 129 130 [Export] ReadDecrementer()131 private ulong ReadDecrementer() 132 { 133 return checked((ulong)machine.ClockSource.GetClockEntry(DecrementerHandler).Value); 134 } 135 136 public bool StartInVle 137 { 138 get; 139 set; 140 } 141 142 [Export] WriteDecrementer(ulong value)143 private void WriteDecrementer(ulong value) 144 { 145 machine.ClockSource.ExchangeClockEntryWith(DecrementerHandler, 146 entry => entry.With(period: value, value: value, enabled: value != 0)); 147 } 148 InternalSetInterrupt(InterruptType interrupt, bool value)149 private void InternalSetInterrupt(InterruptType interrupt, bool value) 150 { 151 lock(irqSync) 152 { 153 if(value) 154 { 155 TlibSetPendingInterrupt((int)interrupt, 1); 156 base.OnGPIO(0, true); 157 return; 158 } 159 if(TlibSetPendingInterrupt((int)interrupt, 0) == 1) 160 { 161 base.OnGPIO(0, false); 162 } 163 } 164 } 165 DecrementerHandler()166 private void DecrementerHandler() 167 { 168 InternalSetInterrupt(InterruptType.Decrementer, true); 169 } 170 171 [Export] IsVleEnabled()172 private uint IsVleEnabled() 173 { 174 //this should present the current state. Now it's a stub only. 175 return StartInVle ? 1u : 0u; 176 } 177 178 // 649: Field '...' is never assigned to, and will always have its default value null 179 #pragma warning disable 649 180 181 [Import] 182 private Func<int, int, int> TlibSetPendingInterrupt; 183 184 [Import] 185 private Action<uint> TlibSetLittleEndianMode; 186 187 #pragma warning restore 649 188 189 private uint tb; 190 private readonly object irqSync; 191 private readonly Endianess initialEndianess; 192 193 // have to be in sync with translation libs 194 private enum InterruptType 195 { 196 Reset = 0, 197 WakeUp, 198 MachineCheck, 199 External, 200 SMI, 201 CritictalExternal, 202 Debug, 203 Thermal, 204 Decrementer, 205 Hypervisor, 206 PIT, 207 FIT, 208 WDT, 209 CriticalDoorbell, 210 Doorbell, 211 PerformanceMonitor 212 } 213 } 214 } 215