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