1 //
2 // Copyright (c) 2010-2022 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.CPU;
12 
13 namespace Antmicro.Renode.Peripherals.Bus
14 {
15     public class WindowMMUBusController : BusControllerProxy
16     {
WindowMMUBusController(IEmulationElement emulationParent, IBusController parentController)17         public WindowMMUBusController(IEmulationElement emulationParent, IBusController parentController) : base(parentController)
18         {
19             this.emulationParent = emulationParent;
20             Windows = new List<MMUWindow>();
21         }
22 
AssertWindowsAreValid()23         public void AssertWindowsAreValid()
24         {
25             for(var i = 0; i < Windows.Count; i++)
26             {
27                 Windows[i].AssertIsValid();
28                 for(var j = 0; j < Windows.Count; j++)
29                 {
30                     if(i != j && Windows[i].ContainsAddress(Windows[j].Start) && Windows[j].Length > 0)
31                     {
32                         emulationParent.Log(LogLevel.Error, "MMUWindows (with indicies {0} and {1}) overlap each other.", i, j);
33                     }
34                 }
35             }
36         }
37 
38         public event Action<ulong, BusAccessPrivileges, int?> OnFault;
39 
40         public List<MMUWindow> Windows { get; }
41 
ValidateOperation(ref ulong address, BusAccessPrivileges accessType, IPeripheral context = null)42         protected override bool ValidateOperation(ref ulong address, BusAccessPrivileges accessType, IPeripheral context = null)
43         {
44             if(TryFindWindowIndex(address, out var index))
45             {
46                 var privileges = Windows[index].Privileges;
47                 if((privileges & accessType) == accessType)
48                 {
49                     address = Windows[index].TranslateAddress(address);
50                     return true;
51                 }
52                 MMUFaultHandler(address, accessType, index);
53             }
54             else
55             {
56                 MMUFaultHandler(address, accessType, null);
57             }
58             return false;
59         }
60 
TryFindWindowIndex(ulong address, out int index)61         private bool TryFindWindowIndex(ulong address, out int index)
62         {
63             for(index = 0; index < Windows.Count; index++)
64             {
65                 if(Windows[index].ContainsAddress(address))
66                 {
67                     if(Windows[index].Valid)
68                     {
69                         return true;
70                     }
71                     else
72                     {
73                         emulationParent.Log(LogLevel.Warning, "The window at index {0} match the address, but isn't validated sucesfully.", index);
74                     }
75                 }
76             }
77             index = -1;
78             return false;
79         }
80 
MMUFaultHandler(ulong address, BusAccessPrivileges accessType, int? windowIndex)81         private void MMUFaultHandler(ulong address, BusAccessPrivileges accessType, int? windowIndex)
82         {
83             emulationParent.Log(LogLevel.Noisy, "IOMMU fault at 0x{0:X} when trying to access as {1}", address, accessType);
84             OnFault?.Invoke(address, accessType, windowIndex);
85 
86             if(windowIndex == null)
87             {
88                 emulationParent.Log(LogLevel.Error, "IOMMU fault - the address 0x{0:X} is not specified in any of the existing ranges", address);
89             }
90         }
91 
92         private readonly IEmulationElement emulationParent;
93 
94         public class MMUWindow
95         {
MMUWindow(IEmulationElement emulationParent)96             public MMUWindow(IEmulationElement emulationParent)
97             {
98                 this.emulationParent = emulationParent;
99             }
100 
ContainsAddress(ulong address)101             public bool ContainsAddress(ulong address)
102             {
103                 return address >= Start && address < End;
104             }
105 
AssertIsValid()106             public void AssertIsValid()
107             {
108                 Valid = true;
109                 if(Start > End)
110                 {
111                     emulationParent.Log(LogLevel.Error, "MMUWindow has start address (0x{0:x}) grater than end address (0x{1:x}).", Start, End);
112                     Valid = false;
113                 }
114 
115                 if(Offset < 0 && Start < (ulong)(-Offset))
116                 {
117                     emulationParent.Log(LogLevel.Error, "MMUWindow has incorrect offset ({0:d}) in relation to the start address (0x{1:x}).", Offset, Start);
118                     Valid = false;
119                 }
120                 else if(Offset > 0 && End > UInt64.MaxValue - (ulong)Offset)
121                 {
122                     emulationParent.Log(LogLevel.Error, "MMUWindow has incorrect offset ({0:d}) in relation to the end address (0x{1:x}).", Offset, End);
123                     Valid = false;
124                 }
125 
126             }
127 
TranslateAddress(ulong address)128             public ulong TranslateAddress(ulong address)
129             {
130                 if(Offset < 0)
131                 {
132                     return checked(address - (ulong)(-Offset));
133                 }
134                 else
135                 {
136                     return checked(address + (ulong)Offset);
137                 }
138             }
139 
140             public ulong Start { get; set; }
141             public ulong End { get; set; }
142             public ulong Length => checked(End - Start);
143             public long Offset { get; set; }
144             public BusAccessPrivileges Privileges { get; set; }
145             public bool Valid { get; private set; }
146 
147             private readonly IEmulationElement emulationParent;
148         }
149     }
150 }
151