1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Utilities.Binding;
10 using System.Collections.Generic;
11 using System;
12 using Antmicro.Renode.Time;
13 using ELFSharp.ELF;
14 using Machine = Antmicro.Renode.Core.Machine;
15 using ELFSharp.ELF.Sections;
16 using System.Linq;
17 using Antmicro.Renode.Logging;
18 using ELFSharp.UImage;
19 
20 namespace Antmicro.Renode.Peripherals.CPU
21 {
22     [GPIO(NumberOfInputs = 1)]
23     public partial class PowerPc : TranslationCPU, ICPUWithHooks
24     {
25         // Note that the reported endianness will be wrong if it is switched at runtime!
PowerPc(string cpuType, IMachine machine, Endianess endianness = Endianess.BigEndian)26         public PowerPc(string cpuType, IMachine machine, Endianess endianness = Endianess.BigEndian) : base(cpuType, machine, endianness)
27         {
28             initialEndianess = endianness;
29             irqSync = new object();
30             machine.ClockSource.AddClockEntry(
31                 new ClockEntry(long.MaxValue / 2, 128000000, DecrementerHandler, this, String.Empty, false, Direction.Descending));
32             TlibSetLittleEndianMode(initialEndianess == Endianess.LittleEndian ? 1u : 0u);
33         }
34 
Reset()35         public override void Reset()
36         {
37             base.Reset();
38             TlibSetLittleEndianMode(initialEndianess == Endianess.LittleEndian ? 1u : 0u);
39         }
40 
InitFromUImage(UImage uImage)41         public override void InitFromUImage(UImage uImage)
42         {
43             this.Log(LogLevel.Warning, "PowerPC VLE mode not implemented for uImage loading.");
44             base.InitFromUImage(uImage);
45         }
46 
InitFromElf(IELF elf)47         public override void InitFromElf(IELF elf)
48         {
49             base.InitFromElf(elf);
50 
51             var bamSection = elf.GetSections<Section<uint>>().FirstOrDefault(x => x.Name == ".__bam_bootarea");
52             if(bamSection != null)
53             {
54                 var bamSectionContents = bamSection.GetContents();
55                 var isValidResetConfigHalfWord = bamSectionContents[1] == 0x5a;
56                 if(!isValidResetConfigHalfWord)
57                 {
58                     this.Log(LogLevel.Warning, "Invalid BAM section, ignoring.");
59                 }
60                 else
61                 {
62                     StartInVle = (bamSectionContents[0] & 0x1) == 1;
63                     this.Log(LogLevel.Info, "Will {0}start in VLE mode.", StartInVle ? "" : "not ");
64                 }
65             }
66         }
67 
OnGPIO(int number, bool value)68         public override void OnGPIO(int number, bool value)
69         {
70             InternalSetInterrupt(InterruptType.External, value);
71         }
72 
73         public override string Architecture { get { return "ppc"; } }
74 
75         public override string GDBArchitecture { get { return "powerpc:common"; } }
76 
77         public override List<GDBFeatureDescriptor> GDBFeatures
78         {
79             get
80             {
81                 var powerCore = new GDBFeatureDescriptor("org.gnu.gdb.power.core");
82                 for(var index = 0u; index < 32; index++)
83                 {
84                     powerCore.Registers.Add(new GDBRegisterDescriptor(index, 32, $"r{index}", "uint32", "general"));
85                 }
86 
87                 powerCore.Registers.Add(new GDBRegisterDescriptor(64, 32, "pc", "code_ptr", "general"));
88                 powerCore.Registers.Add(new GDBRegisterDescriptor(65, 32, "msr", "uint32", "general"));
89                 powerCore.Registers.Add(new GDBRegisterDescriptor(66, 32, "cr", "uint32", "general"));
90                 powerCore.Registers.Add(new GDBRegisterDescriptor(67, 32, "lr", "code_ptr", "general"));
91                 powerCore.Registers.Add(new GDBRegisterDescriptor(68, 32, "ctr", "uint32", "general"));
92                 powerCore.Registers.Add(new GDBRegisterDescriptor(69, 32, "xer", "uint32", "general"));
93 
94                 return new List<GDBFeatureDescriptor>(new GDBFeatureDescriptor[] { powerCore });
95             }
96         }
97 
98         public bool WaitAsNop
99         {
100             get => neverWaitForInterrupt;
101             set
102             {
103                 neverWaitForInterrupt = value;
104             }
105         }
106 
DecodeInterrupt(int number)107         protected override Interrupt DecodeInterrupt(int number)
108         {
109             if(number == 0)
110             {
111                 return Interrupt.Hard;
112             }
113             throw InvalidInterruptNumberException;
114         }
115 
GetExceptionDescription(ulong exceptionIndex)116         protected override string GetExceptionDescription(ulong exceptionIndex)
117         {
118             return ExceptionDescriptionsMap.TryGetValue(exceptionIndex, out var result)
119                 ? result
120                 : base.GetExceptionDescription(exceptionIndex);
121         }
122 
123         [Export]
ReadTbl()124         public uint ReadTbl()
125         {
126             tb += 0x100;
127             return tb;
128         }
129 
130         [Export]
ReadTbu()131         public uint ReadTbu()
132         {
133             return 0;
134         }
135 
136         [Export]
ReadDecrementer()137         private ulong ReadDecrementer()
138         {
139             return checked((uint)machine.ClockSource.GetClockEntry(DecrementerHandler).Value);
140         }
141 
142         public bool StartInVle
143         {
144             get;
145             set;
146         }
147 
148         [Export]
WriteDecrementer(ulong val)149         private void WriteDecrementer(ulong val)
150         {
151             // The API relies on 64-bit values because of PPC64, but the 32-bit PowerPC uses a 32-bit decrementer.
152             var value = (uint)val;
153             machine.ClockSource.ExchangeClockEntryWith(DecrementerHandler,
154                 entry => entry.With(period: value, value: value, enabled: value != 0));
155         }
156 
InternalSetInterrupt(InterruptType interrupt, bool value)157         private void InternalSetInterrupt(InterruptType interrupt, bool value)
158         {
159             lock(irqSync)
160             {
161                 if(value)
162                 {
163                     TlibSetPendingInterrupt((int)interrupt, 1);
164                     base.OnGPIO(0, true);
165                     return;
166                 }
167                 if(TlibSetPendingInterrupt((int)interrupt, 0) == 1)
168                 {
169                     base.OnGPIO(0, false);
170                 }
171             }
172         }
173 
DecrementerHandler()174         private void DecrementerHandler()
175         {
176             InternalSetInterrupt(InterruptType.Decrementer, true);
177         }
178 
179         [Export]
IsVleEnabled()180         private uint IsVleEnabled()
181         {
182             //this should present the current state. Now it's a stub only.
183             return StartInVle ? 1u : 0u;
184         }
185 
186         // 649:  Field '...' is never assigned to, and will always have its default value null
187         #pragma warning disable 649
188 
189         [Import]
190         private Func<int, int, int> TlibSetPendingInterrupt;
191 
192         [Import]
193         private Action<uint> TlibSetLittleEndianMode;
194 
195         #pragma warning restore 649
196 
197         private uint tb;
198         private readonly object irqSync;
199         private readonly Endianess initialEndianess;
200 
201         private readonly Dictionary<ulong, string> ExceptionDescriptionsMap = new Dictionary<ulong, string>
202         {
203             {0, "Critical input"},
204             {1, "Machine check exception"},
205             {2, "Data storage exception"},
206             {3, "Instruction storage exception"},
207             {4, "External input"},
208             {5, "Alignment exception"},
209             {6, "Program exception"},
210             {7, "Floating-point unavailable exception"},
211             {8, "System call exception"},
212             {9, "Auxiliary processor unavailable"},
213             {10, "Decrementer exception"},
214             {11, "Fixed-interval timer interrupt"},
215             {12, "Watchdog timer interrupt"},
216             {13, "Data TLB miss"},
217             {14, "Instruction TLB miss"},
218             {15, "Debug interrupt"},
219             {32, "SPE/embedded floating-point unavailable"},
220             {33, "Embedded floating-point data interrupt"},
221             {34, "Embedded floating-point round interrupt"},
222             {35, "Embedded performance monitor interrupt"},
223             {36, "Embedded doorbell interrupt"},
224             {37, "Embedded doorbell critical interrupt"},
225             {64, "System reset exception"},
226             {65, "Data segment exception"},
227             {66, "Instruction segment exception"},
228             {67, "Hypervisor decrementer exception"},
229             {68, "Trace exception"},
230             {69, "Hypervisor data storage exception"},
231             {70, "Hypervisor instruction storage exception"},
232             {71, "Hypervisor data segment exception"},
233             {72, "Hypervisor instruction segment exception"},
234             {73, "Vector unavailable exception"},
235             {74, "Programmable interval timer interrupt"},
236             {75, "IO error exception"},
237             {76, "Run mode exception"},
238             {77, "Emulation trap exception"},
239             {78, "Instruction fetch TLB miss"},
240             {79, "Data load TLB miss"},
241             {80, "Data store TLB miss"},
242             {81, "Floating-point assist exception"},
243             {82, "Data address breakpoint"},
244             {83, "Instruction address breakpoint"},
245             {84, "System management interrupt"},
246             {85, "Embedded performance monitor interrupt"},
247             {86, "Thermal interrupt"},
248             {87, "Vector assist exception"},
249             {88, "Soft patch exception"},
250             {89, "Maintenance exception"},
251             {90, "Maskable external breakpoint"},
252             {91, "Non maskable external breakpoint"},
253             {92, "Instruction TLB error"},
254             {93, "Data TLB error"},
255             {96, "EOL"},
256             //tlib exceptions: used internally during code translation
257             {512, "Stop translation"},
258             {513, "Branch instruction"},
259             //tlib exceptions: special cases we want to stop translation
260             {514, "Context synchronizing instruction"},
261             {515, "System call in user mode only"},
262             {516, "Conditional stores in user mode"}
263         };
264 
265         // have to be in sync with translation libs
266         private enum InterruptType
267         {
268             Reset = 0,
269             WakeUp,
270             MachineCheck,
271             External,
272             SMI,
273             CritictalExternal,
274             Debug,
275             Thermal,
276             Decrementer,
277             Hypervisor,
278             PIT,
279             FIT,
280             WDT,
281             CriticalDoorbell,
282             Doorbell,
283             PerformanceMonitor
284         }
285     }
286 }
287