├── .gitignore ├── LICENSE ├── MMUit.xcodeproj └── project.pbxproj ├── MMUit ├── MMUit.h ├── MMUit.hpp ├── Primitives.hpp ├── VMAKit.h ├── VMAKit.hpp └── VMAKit │ ├── MMUConfig.h │ ├── MMUConfig.hpp │ ├── PageRelocator.h │ ├── PageRelocator.hpp │ ├── TCR.h │ ├── TCR.hpp │ ├── TTEntry.h │ ├── TTEntry.hpp │ ├── TTWalker.h │ ├── TTWalker.hpp │ ├── VMAKit.cpp │ ├── VMAPlatform.h │ ├── VMAPlatform.hpp │ ├── VMATypes.h │ ├── VMATypes.hpp │ └── VirtualAddress.hpp ├── MMUitTestC └── main.c ├── MMUitTestCPP └── main.cpp ├── README.md ├── Resources ├── usage_details.png └── usage_fake_tt.png ├── include ├── MMUit.h ├── MMUit.hpp ├── Primitives.hpp ├── VMAKit.h ├── VMAKit.hpp └── VMAKit │ ├── MMUConfig.h │ ├── MMUConfig.hpp │ ├── PageRelocator.h │ ├── PageRelocator.hpp │ ├── TCR.h │ ├── TCR.hpp │ ├── TTEntry.h │ ├── TTEntry.hpp │ ├── TTWalker.h │ ├── TTWalker.hpp │ ├── VMAPlatform.h │ ├── VMAPlatform.hpp │ ├── VMATypes.h │ ├── VMATypes.hpp │ └── VirtualAddress.hpp └── lib └── libMMUit.a /.gitignore: -------------------------------------------------------------------------------- 1 | # Project files 2 | *~ 3 | .DS_Store 4 | build 5 | DerivedData 6 | xcuserdata 7 | *.xcworkspace 8 | *.xcuserdatad 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Alexander Hude 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MMUit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | FAF8AAFE1E3C9C3900B51113 /* MMUitUniversal */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = FAF8AAFF1E3C9C3900B51113 /* Build configuration list for PBXAggregateTarget "MMUitUniversal" */; 13 | buildPhases = ( 14 | FAF8AB021E3C9C4E00B51113 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = MMUitUniversal; 19 | productName = MMUitUniversal; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 8A6B0C791E3B06EC00497AAC /* libMMUit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A62B8C41E2C7B6000C123B5 /* libMMUit.a */; }; 25 | 8A6B0C7B1E3EF3F500497AAC /* VMAKit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8A6B0C7A1E3EF3F500497AAC /* VMAKit.cpp */; }; 26 | 8A6B0C7C1E3EF44B00497AAC /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAACB6C31E3B672D0045FB5B /* main.cpp */; }; 27 | 8A6B0C7F1E498C4D00497AAC /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A6B0C7E1E498C4D00497AAC /* libstdc++.tbd */; }; 28 | FA548A301E4C7FD000C2DEF9 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = FA548A2F1E4C7FD000C2DEF9 /* libc++.tbd */; }; 29 | FAF8AAD01E3C578100B51113 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = FAF8AACF1E3C578100B51113 /* main.c */; }; 30 | FAF8AAD41E3C594800B51113 /* libMMUit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A62B8C41E2C7B6000C123B5 /* libMMUit.a */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXCopyFilesBuildPhase section */ 34 | 8A62B8C21E2C7B6000C123B5 /* CopyFiles */ = { 35 | isa = PBXCopyFilesBuildPhase; 36 | buildActionMask = 2147483647; 37 | dstPath = "$(PRODUCT_NAME)"; 38 | dstSubfolderSpec = 16; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | 8A6B0C701E3B06D500497AAC /* CopyFiles */ = { 44 | isa = PBXCopyFilesBuildPhase; 45 | buildActionMask = 2147483647; 46 | dstPath = /usr/share/man/man1/; 47 | dstSubfolderSpec = 0; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 1; 51 | }; 52 | FAF8AACB1E3C578100B51113 /* CopyFiles */ = { 53 | isa = PBXCopyFilesBuildPhase; 54 | buildActionMask = 2147483647; 55 | dstPath = /usr/share/man/man1/; 56 | dstSubfolderSpec = 0; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 1; 60 | }; 61 | /* End PBXCopyFilesBuildPhase section */ 62 | 63 | /* Begin PBXFileReference section */ 64 | 8A374DE01F0C729D0051EC61 /* MMUConfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MMUConfig.hpp; path = VMAKit/MMUConfig.hpp; sourceTree = ""; }; 65 | 8A374DE21F0DAB9D0051EC61 /* MMUConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MMUConfig.h; path = VMAKit/MMUConfig.h; sourceTree = ""; }; 66 | 8A374DE31F0DBAA70051EC61 /* TCR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TCR.h; path = VMAKit/TCR.h; sourceTree = ""; }; 67 | 8A62B8C41E2C7B6000C123B5 /* libMMUit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMMUit.a; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 8A62B8C71E2C7B6000C123B5 /* MMUit.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MMUit.hpp; sourceTree = ""; }; 69 | 8A62B8D11E2C7BD700C123B5 /* VMAKit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VMAKit.hpp; sourceTree = ""; }; 70 | 8A62B8D51E2C826A00C123B5 /* TTWalker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TTWalker.hpp; path = VMAKit/TTWalker.hpp; sourceTree = ""; }; 71 | 8A62B8D71E2C834F00C123B5 /* VMAPlatform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = VMAPlatform.hpp; path = VMAKit/VMAPlatform.hpp; sourceTree = ""; }; 72 | 8A62B8D81E2D6E0A00C123B5 /* VMAKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMAKit.h; sourceTree = ""; }; 73 | 8A62B8D91E2D6E4800C123B5 /* MMUit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMUit.h; sourceTree = ""; }; 74 | 8A62B8DA1E2D9E5800C123B5 /* VirtualAddress.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = VirtualAddress.hpp; path = VMAKit/VirtualAddress.hpp; sourceTree = ""; }; 75 | 8A62B8DB1E2D9E6800C123B5 /* TTEntry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TTEntry.hpp; path = VMAKit/TTEntry.hpp; sourceTree = ""; }; 76 | 8A62B8DC1E2D9F2400C123B5 /* VMATypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = VMATypes.hpp; path = VMAKit/VMATypes.hpp; sourceTree = ""; }; 77 | 8A6B0C6D1E3AEF2300497AAC /* Primitives.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Primitives.hpp; sourceTree = ""; }; 78 | 8A6B0C721E3B06D500497AAC /* MMUitTestCPP */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MMUitTestCPP; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 8A6B0C7A1E3EF3F500497AAC /* VMAKit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VMAKit.cpp; path = VMAKit/VMAKit.cpp; sourceTree = ""; }; 80 | 8A6B0C7D1E3FF24B00497AAC /* PageRelocator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = PageRelocator.hpp; path = VMAKit/PageRelocator.hpp; sourceTree = ""; }; 81 | 8A6B0C7E1E498C4D00497AAC /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libstdc++.tbd"; sourceTree = DEVELOPER_DIR; }; 82 | 8ACA01AF1F0B4BD50058D097 /* TCR.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = TCR.hpp; path = VMAKit/TCR.hpp; sourceTree = ""; }; 83 | FA548A2F1E4C7FD000C2DEF9 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; }; 84 | FA76FB2D1E3C4F29008DF49C /* TTWalker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TTWalker.h; path = VMAKit/TTWalker.h; sourceTree = ""; }; 85 | FAACB6C11E3B5A8C0045FB5B /* VMATypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VMATypes.h; path = VMAKit/VMATypes.h; sourceTree = ""; }; 86 | FAACB6C21E3B5B290045FB5B /* VMAPlatform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VMAPlatform.h; path = VMAKit/VMAPlatform.h; sourceTree = ""; }; 87 | FAACB6C31E3B672D0045FB5B /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = MMUitTestCPP/main.cpp; sourceTree = SOURCE_ROOT; }; 88 | FAE379341E4346A9005E2E24 /* PageRelocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PageRelocator.h; path = VMAKit/PageRelocator.h; sourceTree = ""; }; 89 | FAE379351E43520F005E2E24 /* TTEntry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TTEntry.h; path = VMAKit/TTEntry.h; sourceTree = ""; }; 90 | FAF8AACD1E3C578100B51113 /* MMUitTestC */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MMUitTestC; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | FAF8AACF1E3C578100B51113 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 92 | FAF8AAD61E3C5C5000B51113 /* libc++abi.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++abi.tbd"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib/libc++abi.tbd"; sourceTree = DEVELOPER_DIR; }; 93 | /* End PBXFileReference section */ 94 | 95 | /* Begin PBXFrameworksBuildPhase section */ 96 | 8A62B8C11E2C7B6000C123B5 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | 8A6B0C6F1E3B06D500497AAC /* Frameworks */ = { 104 | isa = PBXFrameworksBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | 8A6B0C791E3B06EC00497AAC /* libMMUit.a in Frameworks */, 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | FAF8AACA1E3C578100B51113 /* Frameworks */ = { 112 | isa = PBXFrameworksBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | FA548A301E4C7FD000C2DEF9 /* libc++.tbd in Frameworks */, 116 | 8A6B0C7F1E498C4D00497AAC /* libstdc++.tbd in Frameworks */, 117 | FAF8AAD41E3C594800B51113 /* libMMUit.a in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | 8A62B8BB1E2C7B6000C123B5 = { 125 | isa = PBXGroup; 126 | children = ( 127 | 8A62B8C61E2C7B6000C123B5 /* MMUit */, 128 | 8A6B0C731E3B06D500497AAC /* MMUitTestCPP */, 129 | FAF8AACE1E3C578100B51113 /* MMUitTestC */, 130 | 8A62B8C51E2C7B6000C123B5 /* Products */, 131 | FAF8AAD51E3C5C5000B51113 /* Frameworks */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 8A62B8C51E2C7B6000C123B5 /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 8A62B8C41E2C7B6000C123B5 /* libMMUit.a */, 139 | 8A6B0C721E3B06D500497AAC /* MMUitTestCPP */, 140 | FAF8AACD1E3C578100B51113 /* MMUitTestC */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | 8A62B8C61E2C7B6000C123B5 /* MMUit */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 8A62B8D31E2C820000C123B5 /* VMAKit */, 149 | 8A6B0C6D1E3AEF2300497AAC /* Primitives.hpp */, 150 | 8A62B8D91E2D6E4800C123B5 /* MMUit.h */, 151 | 8A62B8C71E2C7B6000C123B5 /* MMUit.hpp */, 152 | 8A62B8D81E2D6E0A00C123B5 /* VMAKit.h */, 153 | 8A62B8D11E2C7BD700C123B5 /* VMAKit.hpp */, 154 | ); 155 | path = MMUit; 156 | sourceTree = ""; 157 | }; 158 | 8A62B8D31E2C820000C123B5 /* VMAKit */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | FAACB6C11E3B5A8C0045FB5B /* VMATypes.h */, 162 | 8A62B8DC1E2D9F2400C123B5 /* VMATypes.hpp */, 163 | FAACB6C21E3B5B290045FB5B /* VMAPlatform.h */, 164 | 8A62B8D71E2C834F00C123B5 /* VMAPlatform.hpp */, 165 | 8A62B8DA1E2D9E5800C123B5 /* VirtualAddress.hpp */, 166 | 8A374DE31F0DBAA70051EC61 /* TCR.h */, 167 | 8ACA01AF1F0B4BD50058D097 /* TCR.hpp */, 168 | 8A374DE21F0DAB9D0051EC61 /* MMUConfig.h */, 169 | 8A374DE01F0C729D0051EC61 /* MMUConfig.hpp */, 170 | FAE379351E43520F005E2E24 /* TTEntry.h */, 171 | 8A62B8DB1E2D9E6800C123B5 /* TTEntry.hpp */, 172 | FA76FB2D1E3C4F29008DF49C /* TTWalker.h */, 173 | 8A62B8D51E2C826A00C123B5 /* TTWalker.hpp */, 174 | FAE379341E4346A9005E2E24 /* PageRelocator.h */, 175 | 8A6B0C7D1E3FF24B00497AAC /* PageRelocator.hpp */, 176 | 8A6B0C7A1E3EF3F500497AAC /* VMAKit.cpp */, 177 | ); 178 | name = VMAKit; 179 | sourceTree = ""; 180 | }; 181 | 8A6B0C731E3B06D500497AAC /* MMUitTestCPP */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | FAACB6C31E3B672D0045FB5B /* main.cpp */, 185 | ); 186 | path = MMUitTestCPP; 187 | sourceTree = ""; 188 | }; 189 | FAF8AACE1E3C578100B51113 /* MMUitTestC */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | FAF8AACF1E3C578100B51113 /* main.c */, 193 | ); 194 | path = MMUitTestC; 195 | sourceTree = ""; 196 | }; 197 | FAF8AAD51E3C5C5000B51113 /* Frameworks */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | FA548A2F1E4C7FD000C2DEF9 /* libc++.tbd */, 201 | 8A6B0C7E1E498C4D00497AAC /* libstdc++.tbd */, 202 | FAF8AAD61E3C5C5000B51113 /* libc++abi.tbd */, 203 | ); 204 | name = Frameworks; 205 | sourceTree = ""; 206 | }; 207 | /* End PBXGroup section */ 208 | 209 | /* Begin PBXNativeTarget section */ 210 | 8A62B8C31E2C7B6000C123B5 /* MMUit */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = 8A62B8CD1E2C7B6000C123B5 /* Build configuration list for PBXNativeTarget "MMUit" */; 213 | buildPhases = ( 214 | 8A62B8C01E2C7B6000C123B5 /* Sources */, 215 | 8A62B8C11E2C7B6000C123B5 /* Frameworks */, 216 | 8A62B8C21E2C7B6000C123B5 /* CopyFiles */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | ); 222 | name = MMUit; 223 | productName = MMUit; 224 | productReference = 8A62B8C41E2C7B6000C123B5 /* libMMUit.a */; 225 | productType = "com.apple.product-type.library.static"; 226 | }; 227 | 8A6B0C711E3B06D500497AAC /* MMUitTestCPP */ = { 228 | isa = PBXNativeTarget; 229 | buildConfigurationList = 8A6B0C761E3B06D500497AAC /* Build configuration list for PBXNativeTarget "MMUitTestCPP" */; 230 | buildPhases = ( 231 | 8A6B0C6E1E3B06D500497AAC /* Sources */, 232 | 8A6B0C6F1E3B06D500497AAC /* Frameworks */, 233 | 8A6B0C701E3B06D500497AAC /* CopyFiles */, 234 | ); 235 | buildRules = ( 236 | ); 237 | dependencies = ( 238 | ); 239 | name = MMUitTestCPP; 240 | productName = MMUitTest; 241 | productReference = 8A6B0C721E3B06D500497AAC /* MMUitTestCPP */; 242 | productType = "com.apple.product-type.tool"; 243 | }; 244 | FAF8AACC1E3C578100B51113 /* MMUitTestC */ = { 245 | isa = PBXNativeTarget; 246 | buildConfigurationList = FAF8AAD31E3C578100B51113 /* Build configuration list for PBXNativeTarget "MMUitTestC" */; 247 | buildPhases = ( 248 | FAF8AAC91E3C578100B51113 /* Sources */, 249 | FAF8AACA1E3C578100B51113 /* Frameworks */, 250 | FAF8AACB1E3C578100B51113 /* CopyFiles */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | ); 256 | name = MMUitTestC; 257 | productName = MMUitTestC; 258 | productReference = FAF8AACD1E3C578100B51113 /* MMUitTestC */; 259 | productType = "com.apple.product-type.tool"; 260 | }; 261 | /* End PBXNativeTarget section */ 262 | 263 | /* Begin PBXProject section */ 264 | 8A62B8BC1E2C7B6000C123B5 /* Project object */ = { 265 | isa = PBXProject; 266 | attributes = { 267 | LastUpgradeCheck = 0900; 268 | ORGANIZATIONNAME = FriedAppleTeam; 269 | TargetAttributes = { 270 | 8A62B8C31E2C7B6000C123B5 = { 271 | CreatedOnToolsVersion = 8.2.1; 272 | DevelopmentTeam = ST2L7LXQ36; 273 | ProvisioningStyle = Automatic; 274 | }; 275 | 8A6B0C711E3B06D500497AAC = { 276 | CreatedOnToolsVersion = 8.2.1; 277 | DevelopmentTeam = ST2L7LXQ36; 278 | ProvisioningStyle = Automatic; 279 | }; 280 | FAF8AACC1E3C578100B51113 = { 281 | CreatedOnToolsVersion = 8.2; 282 | ProvisioningStyle = Automatic; 283 | }; 284 | FAF8AAFE1E3C9C3900B51113 = { 285 | CreatedOnToolsVersion = 8.2; 286 | ProvisioningStyle = Automatic; 287 | }; 288 | }; 289 | }; 290 | buildConfigurationList = 8A62B8BF1E2C7B6000C123B5 /* Build configuration list for PBXProject "MMUit" */; 291 | compatibilityVersion = "Xcode 3.2"; 292 | developmentRegion = English; 293 | hasScannedForEncodings = 0; 294 | knownRegions = ( 295 | English, 296 | en, 297 | ); 298 | mainGroup = 8A62B8BB1E2C7B6000C123B5; 299 | productRefGroup = 8A62B8C51E2C7B6000C123B5 /* Products */; 300 | projectDirPath = ""; 301 | projectRoot = ""; 302 | targets = ( 303 | 8A62B8C31E2C7B6000C123B5 /* MMUit */, 304 | 8A6B0C711E3B06D500497AAC /* MMUitTestCPP */, 305 | FAF8AACC1E3C578100B51113 /* MMUitTestC */, 306 | FAF8AAFE1E3C9C3900B51113 /* MMUitUniversal */, 307 | ); 308 | }; 309 | /* End PBXProject section */ 310 | 311 | /* Begin PBXShellScriptBuildPhase section */ 312 | FAF8AB021E3C9C4E00B51113 /* ShellScript */ = { 313 | isa = PBXShellScriptBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | inputPaths = ( 318 | ); 319 | outputPaths = ( 320 | ); 321 | runOnlyForDeploymentPostprocessing = 0; 322 | shellPath = /bin/sh; 323 | shellScript = "# export all environment variables\n# export\n\n# define output folder environment variable\nUNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal\n\n# Build Device and Simulator versions\nxcodebuild -target MMUit -configuration ${CONFIGURATION} -sdk macosx -arch x86_64 BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" PROJECT_TEMP_DIR=\"${PROJECT_TEMP_DIR}\"\nxcodebuild -target MMUit ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos -arch arm64 BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" PROJECT_TEMP_DIR=\"${PROJECT_TEMP_DIR}\"\n\n# remove empty folder created by xcodebuild\nrm -rf \"${PROJECT_DIR}/build\"\n\n# make sure the output directory exists\nmkdir -p \"${UNIVERSAL_OUTPUTFOLDER}\"\n\n# Create universal binary file using lipo\nlipo -create -output \"${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a\" \"${BUILD_DIR}/${CONFIGURATION}/lib${PROJECT_NAME}.a\" \"${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a\"\n\n# Copy lib\nmkdir -p ./lib\ncp \"${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a\" ./lib\n\n# Copy header files\nmkdir -p ./include\ncd ./MMUit; find . -name '*.hpp' -or -name '*.h' | cpio -updm ../include\n"; 324 | }; 325 | /* End PBXShellScriptBuildPhase section */ 326 | 327 | /* Begin PBXSourcesBuildPhase section */ 328 | 8A62B8C01E2C7B6000C123B5 /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 8A6B0C7B1E3EF3F500497AAC /* VMAKit.cpp in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 8A6B0C6E1E3B06D500497AAC /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 8A6B0C7C1E3EF44B00497AAC /* main.cpp in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | FAF8AAC91E3C578100B51113 /* Sources */ = { 345 | isa = PBXSourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | FAF8AAD01E3C578100B51113 /* main.c in Sources */, 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | /* End PBXSourcesBuildPhase section */ 353 | 354 | /* Begin XCBuildConfiguration section */ 355 | 8A62B8CB1E2C7B6000C123B5 /* Debug */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ALWAYS_SEARCH_USER_PATHS = NO; 359 | CLANG_ANALYZER_NONNULL = YES; 360 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 361 | CLANG_CXX_LIBRARY = "libc++"; 362 | CLANG_ENABLE_MODULES = YES; 363 | CLANG_ENABLE_OBJC_ARC = YES; 364 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 365 | CLANG_WARN_BOOL_CONVERSION = YES; 366 | CLANG_WARN_COMMA = YES; 367 | CLANG_WARN_CONSTANT_CONVERSION = YES; 368 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 369 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 370 | CLANG_WARN_EMPTY_BODY = YES; 371 | CLANG_WARN_ENUM_CONVERSION = YES; 372 | CLANG_WARN_INFINITE_RECURSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 376 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 377 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 378 | CLANG_WARN_STRICT_PROTOTYPES = YES; 379 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 380 | CLANG_WARN_UNREACHABLE_CODE = YES; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 383 | COPY_PHASE_STRIP = NO; 384 | DEBUG_INFORMATION_FORMAT = dwarf; 385 | ENABLE_STRICT_OBJC_MSGSEND = YES; 386 | ENABLE_TESTABILITY = YES; 387 | GCC_C_LANGUAGE_STANDARD = gnu99; 388 | GCC_DYNAMIC_NO_PIC = NO; 389 | GCC_NO_COMMON_BLOCKS = YES; 390 | GCC_OPTIMIZATION_LEVEL = 0; 391 | GCC_PREPROCESSOR_DEFINITIONS = ( 392 | "DEBUG=1", 393 | "$(inherited)", 394 | ); 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 402 | MTL_ENABLE_DEBUG_INFO = YES; 403 | ONLY_ACTIVE_ARCH = YES; 404 | SDKROOT = iphoneos; 405 | }; 406 | name = Debug; 407 | }; 408 | 8A62B8CC1E2C7B6000C123B5 /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ALWAYS_SEARCH_USER_PATHS = NO; 412 | CLANG_ANALYZER_NONNULL = YES; 413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 414 | CLANG_CXX_LIBRARY = "libc++"; 415 | CLANG_ENABLE_MODULES = YES; 416 | CLANG_ENABLE_OBJC_ARC = YES; 417 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_COMMA = YES; 420 | CLANG_WARN_CONSTANT_CONVERSION = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 423 | CLANG_WARN_EMPTY_BODY = YES; 424 | CLANG_WARN_ENUM_CONVERSION = YES; 425 | CLANG_WARN_INFINITE_RECURSION = YES; 426 | CLANG_WARN_INT_CONVERSION = YES; 427 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 428 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 429 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 430 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 431 | CLANG_WARN_STRICT_PROTOTYPES = YES; 432 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 433 | CLANG_WARN_UNREACHABLE_CODE = YES; 434 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 435 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 436 | COPY_PHASE_STRIP = NO; 437 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 438 | ENABLE_NS_ASSERTIONS = NO; 439 | ENABLE_STRICT_OBJC_MSGSEND = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu99; 441 | GCC_NO_COMMON_BLOCKS = YES; 442 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 444 | GCC_WARN_UNDECLARED_SELECTOR = YES; 445 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 446 | GCC_WARN_UNUSED_FUNCTION = YES; 447 | GCC_WARN_UNUSED_VARIABLE = YES; 448 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 449 | MTL_ENABLE_DEBUG_INFO = NO; 450 | SDKROOT = iphoneos; 451 | VALIDATE_PRODUCT = YES; 452 | }; 453 | name = Release; 454 | }; 455 | 8A62B8CE1E2C7B6000C123B5 /* Debug */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | DEVELOPMENT_TEAM = ST2L7LXQ36; 459 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 460 | OTHER_LDFLAGS = "-ObjC"; 461 | PRODUCT_NAME = "$(TARGET_NAME)"; 462 | SKIP_INSTALL = YES; 463 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; 464 | VALID_ARCHS = "arm64 armv7 armv7s x86_64"; 465 | }; 466 | name = Debug; 467 | }; 468 | 8A62B8CF1E2C7B6000C123B5 /* Release */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | COPY_PHASE_STRIP = YES; 472 | DEVELOPMENT_TEAM = ST2L7LXQ36; 473 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 474 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 475 | OTHER_LDFLAGS = "-ObjC"; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | SKIP_INSTALL = YES; 478 | STRIP_STYLE = all; 479 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; 480 | VALID_ARCHS = "arm64 armv7 armv7s x86_64"; 481 | }; 482 | name = Release; 483 | }; 484 | 8A6B0C771E3B06D500497AAC /* Debug */ = { 485 | isa = XCBuildConfiguration; 486 | buildSettings = { 487 | CODE_SIGN_IDENTITY = "-"; 488 | DEVELOPMENT_TEAM = ST2L7LXQ36; 489 | GCC_PREPROCESSOR_DEFINITIONS = ( 490 | "DEBUG=1", 491 | "$(inherited)", 492 | ); 493 | MACOSX_DEPLOYMENT_TARGET = 10.11; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | SDKROOT = macosx; 496 | }; 497 | name = Debug; 498 | }; 499 | 8A6B0C781E3B06D500497AAC /* Release */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | CODE_SIGN_IDENTITY = "-"; 503 | DEVELOPMENT_TEAM = ST2L7LXQ36; 504 | MACOSX_DEPLOYMENT_TARGET = 10.11; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SDKROOT = macosx; 507 | }; 508 | name = Release; 509 | }; 510 | FAF8AAD11E3C578100B51113 /* Debug */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | CODE_SIGN_IDENTITY = "-"; 514 | MACOSX_DEPLOYMENT_TARGET = 10.11; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SDKROOT = macosx; 517 | }; 518 | name = Debug; 519 | }; 520 | FAF8AAD21E3C578100B51113 /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | CODE_SIGN_IDENTITY = "-"; 524 | MACOSX_DEPLOYMENT_TARGET = 10.11; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | SDKROOT = macosx; 527 | }; 528 | name = Release; 529 | }; 530 | FAF8AB001E3C9C3900B51113 /* Debug */ = { 531 | isa = XCBuildConfiguration; 532 | buildSettings = { 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | STRIP_STYLE = debugging; 535 | }; 536 | name = Debug; 537 | }; 538 | FAF8AB011E3C9C3900B51113 /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | COPY_PHASE_STRIP = YES; 542 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | }; 545 | name = Release; 546 | }; 547 | /* End XCBuildConfiguration section */ 548 | 549 | /* Begin XCConfigurationList section */ 550 | 8A62B8BF1E2C7B6000C123B5 /* Build configuration list for PBXProject "MMUit" */ = { 551 | isa = XCConfigurationList; 552 | buildConfigurations = ( 553 | 8A62B8CB1E2C7B6000C123B5 /* Debug */, 554 | 8A62B8CC1E2C7B6000C123B5 /* Release */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 8A62B8CD1E2C7B6000C123B5 /* Build configuration list for PBXNativeTarget "MMUit" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 8A62B8CE1E2C7B6000C123B5 /* Debug */, 563 | 8A62B8CF1E2C7B6000C123B5 /* Release */, 564 | ); 565 | defaultConfigurationIsVisible = 0; 566 | defaultConfigurationName = Release; 567 | }; 568 | 8A6B0C761E3B06D500497AAC /* Build configuration list for PBXNativeTarget "MMUitTestCPP" */ = { 569 | isa = XCConfigurationList; 570 | buildConfigurations = ( 571 | 8A6B0C771E3B06D500497AAC /* Debug */, 572 | 8A6B0C781E3B06D500497AAC /* Release */, 573 | ); 574 | defaultConfigurationIsVisible = 0; 575 | defaultConfigurationName = Release; 576 | }; 577 | FAF8AAD31E3C578100B51113 /* Build configuration list for PBXNativeTarget "MMUitTestC" */ = { 578 | isa = XCConfigurationList; 579 | buildConfigurations = ( 580 | FAF8AAD11E3C578100B51113 /* Debug */, 581 | FAF8AAD21E3C578100B51113 /* Release */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | FAF8AAFF1E3C9C3900B51113 /* Build configuration list for PBXAggregateTarget "MMUitUniversal" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | FAF8AB001E3C9C3900B51113 /* Debug */, 590 | FAF8AB011E3C9C3900B51113 /* Release */, 591 | ); 592 | defaultConfigurationIsVisible = 0; 593 | defaultConfigurationName = Release; 594 | }; 595 | /* End XCConfigurationList section */ 596 | }; 597 | rootObject = 8A62B8BC1E2C7B6000C123B5 /* Project object */; 598 | } 599 | -------------------------------------------------------------------------------- /MMUit/MMUit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit.h" 12 | -------------------------------------------------------------------------------- /MMUit/MMUit.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit.hpp" 12 | 13 | #include "Primitives.hpp" 14 | -------------------------------------------------------------------------------- /MMUit/Primitives.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | class Primitives 14 | { 15 | public: 16 | Primitives() 17 | {} 18 | 19 | ~Primitives() 20 | {} 21 | 22 | // Read 23 | 24 | virtual uint8_t read8(virt_addr_t address) { assert(0); } 25 | virtual uint16_t read16(virt_addr_t address) { assert(0); } 26 | virtual uint32_t read32(virt_addr_t address) { assert(0); } 27 | virtual uint64_t read64(virt_addr_t address) { assert(0); } 28 | virtual uintptr_t readAddress(virt_addr_t address) { assert(0); } 29 | 30 | // Write 31 | 32 | virtual void write8(virt_addr_t address, uint8_t data) { assert(0); } 33 | virtual void write16(virt_addr_t address, uint16_t data) { assert(0); } 34 | virtual void write32(virt_addr_t address, uint32_t data) { assert(0); } 35 | virtual void write64(virt_addr_t address, uint64_t data) { assert(0); } 36 | virtual void writeAddress(virt_addr_t address, uintptr_t data) { assert(0); } 37 | 38 | // Function call 39 | 40 | virtual uintptr_t callFunction(virt_addr_t address) { assert(0); } 41 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0) { assert(0); } 42 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1) { assert(0); } 43 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) { assert(0); } 44 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) { assert(0); } 45 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { assert(0); } 46 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) { assert(0); } 47 | 48 | // Memory allocation 49 | 50 | virtual virt_addr_t allocInPhysicalMemory(uint32_t size) { assert(0); } 51 | virtual bool deallocInPhysicalMemory(virt_addr_t address, uint32_t size) { assert(0); } 52 | 53 | // Memory copy 54 | 55 | virtual void copyInKernel(virt_addr_t dst, virt_addr_t src, uint32_t size) { assert(0); } 56 | 57 | // Virtual <-> Physical address conversion 58 | 59 | phys_addr_t virtualToPhysical (virt_addr_t address) { assert(0); } 60 | virt_addr_t physicalToVirtual (phys_addr_t address) { assert(0); } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /MMUit/VMAKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit/VMAPlatform.h" 12 | #include "VMAKit/VMATypes.h" 13 | #include "VMAKit/TCR.h" 14 | 15 | #include "VMAKit/MMUConfig.h" 16 | #include "VMAKit/TTWalker.h" 17 | #include "VMAKit/PageRelocator.h" 18 | -------------------------------------------------------------------------------- /MMUit/VMAKit.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit/VMAPlatform.hpp" 12 | #include "VMAKit/VMATypes.hpp" 13 | #include "VMAKit/VirtualAddress.hpp" 14 | #include "VMAKit/TCR.hpp" 15 | #include "VMAKit/TTEntry.hpp" 16 | 17 | #include "VMAKit/MMUConfig.hpp" 18 | #include "VMAKit/TTWalker.hpp" 19 | #include "VMAKit/PageRelocator.hpp" 20 | -------------------------------------------------------------------------------- /MMUit/VMAKit/MMUConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TCR.h" 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #ifndef __cplusplus 20 | 21 | typedef struct 22 | { 23 | TTGranule granule; 24 | TTLevel initial_level; 25 | uint32_t region_size_offset; 26 | } MMUConfig; 27 | 28 | #endif 29 | 30 | typedef struct { 31 | MMUConfig mmu_config; 32 | virt_addr_t table_base; 33 | 34 | uintptr_t cb_user_data; 35 | 36 | // primitives 37 | uintptr_t (*read_address)(virt_addr_t address); 38 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 39 | 40 | // cpp object 41 | void* object; 42 | } mmuconfigparser; 43 | 44 | void mmuconfig_Init(mmuconfigparser* configparser); 45 | void mmuconfig_SetTCR_EL1(mmuconfigparser* configparser, tcr_el1_t tcr_value); 46 | MMUConfig mmuconfig_GetConfigFor(mmuconfigparser* configparser, ExceptionLevel el); 47 | void mmuconfig_Clear(mmuconfigparser* configparser); 48 | void mmuconfig_Close(mmuconfigparser* configparcer); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /MMUit/VMAKit/MMUConfig.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | #include "TCR.hpp" 14 | 15 | struct MMUConfig 16 | { 17 | TTGranule granule; 18 | TTLevel initialLevel; 19 | uint32_t regionSizeOffset; 20 | }; 21 | 22 | class MMUConfigParser 23 | { 24 | public: 25 | MMUConfigParser() 26 | { 27 | clear(); 28 | }; 29 | ~MMUConfigParser() 30 | {}; 31 | 32 | void setTCR_EL1(TCR_EL1 tcr_el1) 33 | { 34 | // Parse TCR for TTBR0 35 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL0)]; 36 | 37 | config->regionSizeOffset = tcr_el1.getT0SZ(); 38 | switch (tcr_el1.getTG0()) { 39 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 40 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 41 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 42 | default: assert(0); 43 | } 44 | 45 | if (config->regionSizeOffset) { 46 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 47 | } 48 | 49 | // Parse TCR for TTBR1 50 | config = &m_configs[uint32_t(ExceptionLevel::EL1)]; 51 | 52 | config->regionSizeOffset = tcr_el1.getT1SZ(); 53 | switch (tcr_el1.getTG1()) { 54 | case TCR_TG1::Granule4K: config->granule = TTGranule::Granule4K; break; 55 | case TCR_TG1::Granule16K: config->granule = TTGranule::Granule16K; break; 56 | case TCR_TG1::Granule64K: config->granule = TTGranule::Granule64K; break; 57 | default: assert(0); 58 | } 59 | 60 | if (config->regionSizeOffset) { 61 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 62 | } 63 | } 64 | 65 | void setTCR_EL1(tcr_el1_t tcr_value) 66 | { 67 | setTCR_EL1(TCR_EL1(tcr_value)); 68 | } 69 | 70 | void setTCR_EL2(TCR_EL2 tcr_el2) 71 | { 72 | // Parse TCR for TTBR0 73 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL2)]; 74 | 75 | config->regionSizeOffset = tcr_el2.getT0SZ(); 76 | switch (tcr_el2.getTG0()) { 77 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 78 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 79 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 80 | default: assert(0); 81 | } 82 | 83 | if (config->regionSizeOffset) { 84 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 85 | } 86 | } 87 | 88 | void setTCR_EL2(tcr_el2_t tcr_value) 89 | { 90 | setTCR_EL2(TCR_EL2(tcr_value)); 91 | } 92 | 93 | void setTCR_EL3(TCR_EL3 tcr_el3) 94 | { 95 | // Parse TCR for TTBR0 96 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL3)]; 97 | 98 | config->regionSizeOffset = tcr_el3.getT0SZ(); 99 | switch (tcr_el3.getTG0()) { 100 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 101 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 102 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 103 | default: assert(0); 104 | } 105 | 106 | if (config->regionSizeOffset) { 107 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 108 | } 109 | } 110 | 111 | void setTCR_EL3(tcr_el3_t tcr_value) 112 | { 113 | setTCR_EL3(TCR_EL3(tcr_value)); 114 | } 115 | 116 | MMUConfig getConfigFor(ExceptionLevel el) 117 | { 118 | return m_configs[uint32_t(el)]; 119 | } 120 | 121 | void clear() 122 | { 123 | for (uint32_t i = 0; i < uint32_t(ExceptionLevel::Count); i++) 124 | m_configs[i] = { .granule = TTGranule::Undefined, .initialLevel = TTLevel::Undefined, .regionSizeOffset = 0 }; 125 | } 126 | 127 | private: 128 | 129 | TTLevel getInitialLevel(TTGranule granule, uint32_t regionOffsetSize) 130 | { 131 | assert(regionOffsetSize >= 16 && regionOffsetSize <= 39); 132 | 133 | switch (granule) { 134 | case TTGranule::Granule4K: 135 | { 136 | // Table D4-11 TCR.TnSZ values and IA ranges, 4K granule with no concatenation of tables 137 | switch (regionOffsetSize) { 138 | case 16 ... 24: return TTLevel::Level0; 139 | case 25 ... 33: return TTLevel::Level1; 140 | case 34 ... 39: return TTLevel::Level2; 141 | default: break; 142 | }; 143 | break; 144 | } 145 | case TTGranule::Granule16K: 146 | { 147 | // Table D4-14 TCR.TnSZ values and IA ranges, 16K granule with no concatenation of tables 148 | switch (regionOffsetSize) { 149 | case 16: return TTLevel::Level0; 150 | case 17 ... 27: return TTLevel::Level1; 151 | case 28 ... 38: return TTLevel::Level2; 152 | case 39: return TTLevel::Level3; 153 | default: break; 154 | }; 155 | break; 156 | } 157 | case TTGranule::Granule64K: 158 | { 159 | // Table D4-17 TCR.TnSZ values and IA ranges, 64K granule with no concatenation of tables 160 | switch (regionOffsetSize) { 161 | case 16 ... 21: return TTLevel::Level1; 162 | case 22 ... 34: return TTLevel::Level2; 163 | case 35 ... 39: return TTLevel::Level3; 164 | default: break; 165 | } 166 | break; 167 | } 168 | 169 | default: assert(0); 170 | } 171 | 172 | return TTLevel::Undefined; 173 | } 174 | 175 | private: 176 | 177 | MMUConfig m_configs[uint32_t(ExceptionLevel::Count)]; 178 | }; 179 | -------------------------------------------------------------------------------- /MMUit/VMAKit/PageRelocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TTEntry.h" 14 | #include "MMUConfig.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct 21 | { 22 | MMUConfig mmu_config; 23 | virt_addr_t table_base; 24 | 25 | uintptr_t cb_user_data; 26 | 27 | // primitives 28 | uintptr_t (*read_address)(virt_addr_t address); 29 | void (*write_address)(virt_addr_t address, virt_addr_t data); 30 | 31 | void (*copy_in_kernel)(virt_addr_t dst, virt_addr_t src, uint32_t size); 32 | 33 | virt_addr_t (*alloc_in_physical_memory)(uint32_t size); 34 | bool (*dealloc_in_physical_memory)(virt_addr_t address, uint32_t size); 35 | 36 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 37 | virt_addr_t (*virtual_to_physical)(phys_addr_t address); 38 | 39 | // cpp object 40 | void* object; 41 | } pagerelocator; 42 | 43 | typedef ttentry_t (*pagerelocator_callback)(TTLevel level, TTEntryDetails* oldEntry, TTEntryDetails* newEntry, uintptr_t user_data); 44 | 45 | void pagerelocator_Init(pagerelocator* relocator); 46 | bool pagerelocator_RelocatePage(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback); 47 | virt_addr_t pagerelocator_PreparePageRelocation(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback); 48 | bool pagerelocator_CompleteRelocation(pagerelocator* relocator); 49 | bool pagerelocator_CancelRelocation(pagerelocator* relocator); 50 | bool pagerelocator_RestorePage(pagerelocator* relocator, virt_addr_t address); 51 | void pagerelocator_Close(pagerelocator* relocator); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /MMUit/VMAKit/PageRelocator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "TTWalker.hpp" 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class PageRelocator : public PRIMITIVES 18 | { 19 | public: 20 | 21 | // Relocation callback is called when table translation entry is about to be updated 22 | using RelocatorCallback = std::function; 23 | static const RelocatorCallback DefaultCallback; 24 | 25 | public: 26 | 27 | PageRelocator() = delete; 28 | 29 | PageRelocator(MMUConfig mmuConfig, virt_addr_t tableBase) 30 | : m_mmuConfig(mmuConfig), m_tableBase(tableBase), 31 | kPageSize(uint32_t(mmuConfig.granule)), kPageMask(kPageSize - 1) 32 | {} 33 | 34 | bool isPageRelocatedFor(virt_addr_t address) 35 | { 36 | virt_addr_t targetPageAddress = address & ~kPageMask; 37 | 38 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) != m_relocatedPages.end()) 39 | return true; 40 | else 41 | return false; 42 | } 43 | 44 | bool isRelocationPendingFor(virt_addr_t address) 45 | { 46 | // check if there is a pending relocation 47 | if (m_relocationPending == false) 48 | return false; 49 | 50 | virt_addr_t targetPageAddress = address & ~kPageMask; 51 | 52 | // check if target page matches one in pending relocation 53 | if (m_stagingInfo.targetPageVA != targetPageAddress) 54 | return false; 55 | else 56 | return true; 57 | } 58 | 59 | bool relocatePageFor(virt_addr_t address, RelocatorCallback callback = DefaultCallback) 60 | { 61 | virt_addr_t page = preparePageRelocationFor(address, callback); 62 | if (page == kInvalidAddress) 63 | return false; 64 | 65 | return completeRelocation(); 66 | } 67 | 68 | virt_addr_t preparePageRelocationFor(virt_addr_t address, RelocatorCallback callback = DefaultCallback) 69 | { 70 | virt_addr_t targetPageAddress = address & ~kPageMask; 71 | 72 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) != m_relocatedPages.end()) 73 | return false; 74 | 75 | // cancel pending relocations 76 | cancelRelocation(); 77 | 78 | TTWalker walker(m_mmuConfig, m_tableBase); 79 | WalkResult result = walker.walkTo(address, [this, callback] (WalkPosition* position, TTGenericEntry* entry) { 80 | // safety checks 81 | if (position == nullptr || entry == nullptr) 82 | return WalkOperation::Stop; 83 | 84 | // get next level page 85 | virt_addr_t nextLevelPA = entry->getOutputAddress(); 86 | 87 | // check if page is already relocated 88 | if (m_relocationMap.find(nextLevelPA) != m_relocationMap.end()) 89 | { 90 | m_relocationMap[nextLevelPA].refCount++; 91 | return WalkOperation::Continue; 92 | } 93 | 94 | // allocate new page 95 | virt_addr_t newPageVA = this->allocInPhysicalMemory(kPageSize); 96 | assert((newPageVA & kPageMask) == 0); 97 | 98 | virt_addr_t nextLevelVA = this->physicalToVirtual(nextLevelPA); 99 | 100 | // clone page content 101 | this->copyInKernel(newPageVA, nextLevelVA, kPageSize); 102 | 103 | // get PA of allocated page 104 | phys_addr_t newPagePA = this->virtualToPhysical(newPageVA); 105 | 106 | // save original descriptor for current level 107 | ttentry_t oldEntryDescriptor = entry->getDescriptor(); 108 | 109 | ttentry_t newEntryDescriptor; 110 | TTGenericEntry* oldEntry = entry->clone(); 111 | 112 | // update entry PA 113 | entry->setOutputAddress(newPagePA); 114 | 115 | // apply external modifications 116 | newEntryDescriptor = callback(position->level, oldEntry, entry); 117 | delete oldEntry; 118 | 119 | Relocation relocation = { 120 | .originalEntry = oldEntryDescriptor, 121 | .allocatedPage = newPageVA, 122 | .refCount = 1 123 | }; 124 | 125 | if (entry->isPageDescriptor() == false) 126 | { 127 | // write TT entry back 128 | this->writeAddress(position->tableAddress + position->entryOffset, newEntryDescriptor); 129 | 130 | // save relocated page 131 | m_relocationMap[newPagePA] = relocation; 132 | } 133 | else 134 | { 135 | // stage relocation 136 | m_stagingInfo.allocatedPagePA = newPagePA; 137 | m_stagingInfo.allocatedPageEntry = newEntryDescriptor; 138 | m_stagingInfo.entryPosition = *position; 139 | m_stagingInfo.relocation = relocation; 140 | } 141 | 142 | return WalkOperation::Continue; 143 | }); 144 | 145 | if (result.getType() == WalkResultType::Complete) 146 | { 147 | m_stagingInfo.targetPageVA = targetPageAddress; 148 | m_relocationPending = true; 149 | 150 | return m_stagingInfo.relocation.allocatedPage; 151 | } 152 | else 153 | { 154 | restorePageFor(targetPageAddress); 155 | 156 | return kInvalidAddress; 157 | } 158 | } 159 | 160 | bool completeRelocation() 161 | { 162 | if (m_relocationPending == false) 163 | return false; 164 | 165 | // write TT entry back 166 | this->writeAddress(m_stagingInfo.entryPosition.tableAddress + m_stagingInfo.entryPosition.entryOffset, m_stagingInfo.allocatedPageEntry); 167 | 168 | // save relocated page 169 | m_relocationMap[m_stagingInfo.allocatedPagePA] = m_stagingInfo.relocation; 170 | 171 | // add page to relocated pages 172 | m_relocatedPages.push_back(m_stagingInfo.targetPageVA); 173 | 174 | m_relocationPending = false; 175 | 176 | return true; 177 | } 178 | 179 | bool cancelRelocation() 180 | { 181 | if (m_relocationPending == false) 182 | return false; 183 | 184 | // deallocate page 185 | this->deallocInPhysicalMemory(m_stagingInfo.relocation.allocatedPage, kPageSize); 186 | 187 | // restore TT entries 188 | bool result = restorePageFor(m_stagingInfo.targetPageVA); 189 | 190 | m_relocationPending = false; 191 | 192 | return result; 193 | } 194 | 195 | bool restorePageFor(virt_addr_t address) 196 | { 197 | virt_addr_t targetPageAddress = address & ~kPageMask; 198 | 199 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) == m_relocatedPages.end()) 200 | { 201 | // unable to find page, check if there is a pending relocation 202 | if (m_relocationPending == false) 203 | return false; 204 | 205 | // check if target page matches one in pending relocation 206 | if (m_stagingInfo.targetPageVA != targetPageAddress) 207 | return false; 208 | } 209 | 210 | TTWalker walker(m_mmuConfig, m_tableBase); 211 | 212 | bool result = walker.reverseWalkFrom(address, [this] (WalkPosition* position, TTGenericEntry* entry) { 213 | // safety checks 214 | if (position == nullptr || entry == nullptr) 215 | return WalkOperation::Stop; 216 | 217 | // get level page 218 | virt_addr_t levelPA = entry->getOutputAddress(); 219 | if (m_relocationMap.find(levelPA) == m_relocationMap.end()) 220 | return WalkOperation::Continue; 221 | 222 | // get original descriptor 223 | Relocation& relocation = m_relocationMap[levelPA]; 224 | 225 | if (relocation.refCount == 1) 226 | { 227 | // restore TT entry 228 | this->writeAddress(position->tableAddress + position->entryOffset, relocation.originalEntry); 229 | 230 | // deallocate page 231 | this->deallocInPhysicalMemory(relocation.allocatedPage, kPageSize); 232 | 233 | // remove page from relocation map 234 | m_relocationMap.erase(levelPA); 235 | } 236 | else 237 | { 238 | relocation.refCount--; 239 | } 240 | 241 | return WalkOperation::Continue; 242 | }); 243 | 244 | if (m_relocationPending == false) 245 | { 246 | // remove page from relocated pages 247 | m_relocatedPages.erase(std::remove(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress), m_relocatedPages.end()); 248 | } 249 | 250 | return result; 251 | } 252 | 253 | private: 254 | 255 | const uint32_t kPageSize; 256 | const virt_addr_t kPageMask; 257 | 258 | MMUConfig m_mmuConfig; 259 | virt_addr_t m_tableBase = kInvalidAddress; 260 | 261 | struct Relocation 262 | { 263 | ttentry_t originalEntry; 264 | virt_addr_t allocatedPage; 265 | uint32_t refCount; 266 | }; 267 | 268 | struct StagingInfo 269 | { 270 | virt_addr_t targetPageVA; 271 | phys_addr_t allocatedPagePA; 272 | ttentry_t allocatedPageEntry; 273 | WalkPosition entryPosition; 274 | Relocation relocation; 275 | }; 276 | 277 | bool m_relocationPending = false; 278 | StagingInfo m_stagingInfo; 279 | 280 | std::vector m_relocatedPages; 281 | std::map m_relocationMap; 282 | }; 283 | 284 | template 285 | const typename PageRelocator::RelocatorCallback PageRelocator::DefaultCallback = 286 | [] (TTLevel level, TTGenericEntry* entry) -> ttentry_t 287 | { 288 | assert(entry != nullptr); 289 | return entry->getDescriptor(); 290 | }; 291 | -------------------------------------------------------------------------------- /MMUit/VMAKit/TCR.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #ifndef __cplusplus 12 | 13 | typedef uint64_t tcr_el1_t; 14 | typedef uint32_t tcr_el2_t; 15 | typedef uint32_t tcr_el3_t; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /MMUit/VMAKit/TCR.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | 14 | enum class TCR_EPD0 : uint32_t // Translation table walk disable for translations using TTBR0_EL1. 15 | { 16 | PerformWalk = 0b0, // Perform translation table walks using TTBR0_EL1. 17 | FaultOnMiss = 0b1, // A TLB miss on an address that is translated using TTBR0_EL1 generates a Translation fault. No translation table walk is performed. 18 | }; 19 | 20 | enum class TCR_IRGN0 : uint32_t // Inner cacheability attribute for memory associated with translation table walks using TTBR0_EL1. 21 | { 22 | InnerNC = 0b00, // Normal memory, Inner Non-cacheable 23 | InnerWB_RA_WA_C = 0b01, // Normal memory, Inner Write-Back Read-Allocate Write-Allocate Cacheable 24 | InnerWT_RA_NWA_C = 0b10, // Normal memory, Inner Write-Through Read-Allocate No Write-Allocate Cacheable 25 | InnerWB_RA_NWA_C = 0b11, // Normal memory, Inner Write-Back Read-Allocate No Write-Allocate Cacheable 26 | }; 27 | 28 | enum class TCR_ORGN0 : uint32_t // Outer cacheability attribute for memory associated with translation table walks using TTBR0_EL1. 29 | 30 | { 31 | OuterNC = 0b00, // Normal memory, Outer Non-cacheable 32 | OuterWB_RA_WA_C = 0b01, // Normal memory, Outer Write-Back Read-Allocate Write-Allocate Cacheable 33 | OuterWT_RA_NWA_C = 0b10, // Normal memory, Outer Write-Through Read-Allocate No Write-Allocate Cacheable 34 | OuterWB_RA_NWA_C = 0b11, // Normal memory, Outer Write-Back Read-Allocate No Write-Allocate Cacheable 35 | }; 36 | 37 | enum class TCR_SH0 : uint32_t // Shareability attribute for memory associated with translation table walks using TTBR0_EL1. 38 | { 39 | NonShareable = 0b00, // Non-shareable 40 | OuterShareable = 0b10, // Outer Shareable 41 | InnerShareable = 0b11, // Inner Shareable 42 | }; 43 | 44 | enum class TCR_TG0 : uint32_t // Granule size for the TTBR0_EL1. 45 | { 46 | Granule4K = 0b00, // 4KB 47 | Granule64K = 0b01, // 64KB 48 | Granule16K = 0b10, // 16KB 49 | }; 50 | 51 | enum class TCR_A1 : uint32_t // Selects whether TTBR0_EL1 or TTBR1_EL1 defines the ASID. 52 | { 53 | ASID_TTBR0 = 0b0, // TTBR0_EL1.ASID defines the ASID. 54 | ASID_TTBR1 = 0b1, // TTBR1_EL1.ASID defines the ASID. 55 | }; 56 | 57 | enum class TCR_EPD1 : uint32_t // Translation table walk disable for translations using TTBR1_EL1. 58 | { 59 | PerformWalk = 0b0, // Perform translation table walks using TTBR1_EL1. 60 | FaultOnMiss = 0b1, // A TLB miss on an address that is translated using TTBR1_EL1 generates a Translation fault. No translation table walk is performed. 61 | }; 62 | 63 | enum class TCR_IRGN1 : uint32_t // Inner cacheability attribute for memory associated with translation table walks using TTBR1_EL1. 64 | { 65 | InnerNC = 0b00, // Normal memory, Inner Non-cacheable 66 | InnerWB_RA_WA_C = 0b01, // Normal memory, Inner Write-Back Read-Allocate Write-Allocate Cacheable 67 | InnerWT_RA_NWA_C = 0b10, // Normal memory, Inner Write-Through Read-Allocate No Write-Allocate Cacheable 68 | InnerWB_RA_NWA_C = 0b11, // Normal memory, Inner Write-Back Read-Allocate No Write-Allocate Cacheable 69 | }; 70 | 71 | enum class TCR_ORGN1 : uint32_t // Outer cacheability attribute for memory associated with translation table walks using TTBR1_EL1. 72 | { 73 | OuterNC = 0b00, // Normal memory, Outer Non-cacheable 74 | OuterWB_RA_WA_C = 0b01, // Normal memory, Outer Write-Back Read-Allocate Write-Allocate Cacheable 75 | OuterWT_RA_NWA_C = 0b10, // Normal memory, Outer Write-Through Read-Allocate No Write-Allocate Cacheable 76 | OuterWB_RA_NWA_C = 0b11, // Normal memory, Outer Write-Back Read-Allocate No Write-Allocate Cacheable 77 | }; 78 | 79 | enum class TCR_SH1 : uint32_t // Shareability attribute for memory associated with translation table walks using TTBR1_EL1. 80 | { 81 | NonShareable = 0b00, // Non-shareable 82 | OuterShareable = 0b10, // Outer Shareable 83 | InnerShareable = 0b11, // Inner Shareable 84 | }; 85 | 86 | enum class TCR_TG1 : uint32_t // Granule size for the TTBR1_EL1. 87 | { 88 | Granule16K = 0b01, // 16KB 89 | Granule4K = 0b10, // 4KB 90 | Granule64K = 0b11, // 64KB 91 | }; 92 | 93 | enum class TCR_PS : uint32_t // Intermediate Physical Address Size. 94 | { 95 | Size4GB = 0b000, // 32 bits, 4GB. 96 | Size64GB = 0b001, // 36 bits, 64GB. 97 | Size1TB = 0b010, // 40 bits, 1TB. 98 | Size4TB = 0b011, // 42 bits, 4TB. 99 | Size16TB = 0b100, // 44 bits, 16TB. 100 | Size256TB = 0b101, // 48 bits, 256TB. 101 | }; 102 | 103 | using TCR_IPS = TCR_PS; 104 | 105 | enum class TCR_AS : uint32_t // ASID Size 106 | { 107 | ASID_8bit = 0b0, // 8 bit 108 | ASID_16bit = 0b1, // 16 bit 109 | }; 110 | 111 | enum class TCR_TBI : uint32_t // Top Byte ignored, indicates whether the top byte of an address is used for address match for the TTBR0_ELx region 112 | { 113 | TopByteUsed = 0b0, // Top Byte used in the address calculation. 114 | TopByteIgnored = 0b1, // Top Byte ignored in the address calculation. 115 | }; 116 | 117 | template union TCRFormat {}; 118 | 119 | // MARK: - TCR_EL1 120 | // D7.2.84. TCR_EL1, Translation Control Register (EL1) 121 | 122 | using tcr_el1_t = uint64_t; 123 | 124 | template<> 125 | union TCRFormat 126 | { 127 | using tcr_value_t = tcr_el1_t; 128 | 129 | struct 130 | { 131 | tcr_value_t T0SZ : 6; // [5:0] 132 | tcr_value_t RES0_1 : 1; // [6] 133 | TCR_EPD0 EPD0 : 1; // [7] 134 | TCR_IRGN0 IRGN0 : 2; // [9:8] 135 | TCR_ORGN0 ORGN0 : 2; // [11:10] 136 | TCR_SH0 SH0 : 2; // [13:12] 137 | TCR_TG0 TG0 : 2; // [15:14] 138 | tcr_value_t T1SZ : 6; // [21:16] 139 | TCR_A1 A1 : 1; // [22] 140 | TCR_EPD1 EPD1 : 1; // [23] 141 | TCR_IRGN1 IRGN1 : 2; // [25:24] 142 | TCR_ORGN1 ORGN1 : 2; // [27:26] 143 | TCR_SH1 SH1 : 2; // [29:28] 144 | TCR_TG1 TG1 : 2; // [31:30] 145 | TCR_IPS IPS : 3; // [34:32] 146 | tcr_value_t RES0_2 : 1; // [35] 147 | TCR_AS AS : 1; // [36] 148 | TCR_TBI TBI0 : 1; // [37] 149 | TCR_TBI TBI1 : 1; // [38] 150 | tcr_value_t RES0_3 : 25; // [63:39] 151 | } details; 152 | 153 | tcr_value_t value; 154 | 155 | TCRFormat(tcr_value_t value) : value(value) { } 156 | 157 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 158 | TCR_EPD0 getEPD0() { return details.EPD0; } 159 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 160 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 161 | TCR_SH0 getSH0() { return details.SH0; } 162 | TCR_TG0 getTG0() { return details.TG0; } 163 | uint32_t getT1SZ() { return uint32_t(details.T1SZ); } 164 | TCR_A1 getA1() { return details.A1; } 165 | TCR_EPD1 getEPD1() { return details.EPD1; } 166 | TCR_IRGN1 getIRGN1() { return details.IRGN1; } 167 | TCR_ORGN1 getORGN1() { return details.ORGN1; } 168 | TCR_SH1 getSH1() { return details.SH1; } 169 | TCR_TG1 getTG1() { return details.TG1; } 170 | TCR_IPS getIPS() { return details.IPS; } 171 | TCR_AS getAS() { return details.AS; } 172 | TCR_TBI getTBI0() { return details.TBI0; } 173 | TCR_TBI getTBI1() { return details.TBI1; } 174 | }; 175 | 176 | // MARK: - TCR_EL2 177 | // D7.2.85. TCR_EL2, Translation Control Register (EL2) 178 | 179 | using tcr_el2_t = uint32_t; 180 | 181 | template<> 182 | union TCRFormat 183 | { 184 | using tcr_value_t = tcr_el2_t; 185 | 186 | struct 187 | { 188 | tcr_value_t T0SZ : 6; // [5:0] 189 | tcr_value_t RES0_1 : 2; // [7:6] 190 | TCR_IRGN0 IRGN0 : 2; // [9:8] 191 | TCR_ORGN0 ORGN0 : 2; // [11:10] 192 | TCR_SH0 SH0 : 2; // [13:12] 193 | TCR_TG0 TG0 : 2; // [15:14] 194 | TCR_PS PS : 3; // [18:16] 195 | tcr_value_t RES0_2 : 1; // [19] 196 | TCR_TBI TBI : 1; // [20] 197 | tcr_value_t RES0_3 : 2; // [22:21] 198 | tcr_value_t RES1_1 : 1; // [23] 199 | tcr_value_t RES0_4 : 7; // [30:24] 200 | tcr_value_t RES1_2 : 1; // [31] 201 | } details; 202 | 203 | tcr_value_t value; 204 | 205 | TCRFormat(tcr_value_t value) : value(value) { } 206 | 207 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 208 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 209 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 210 | TCR_SH0 getSH0() { return details.SH0; } 211 | TCR_TG0 getTG0() { return details.TG0; } 212 | TCR_PS getPS() { return details.PS; } 213 | TCR_TBI getTBI() { return details.TBI; } 214 | }; 215 | 216 | // MARK: - TCR_EL3 217 | // D7.2.85. TCR_EL3, Translation Control Register (EL3) 218 | 219 | using tcr_el3_t = uint32_t; 220 | 221 | template<> 222 | union TCRFormat 223 | { 224 | using tcr_value_t = tcr_el3_t; 225 | 226 | struct 227 | { 228 | tcr_value_t T0SZ : 6; // [5:0] 229 | tcr_value_t RES0_1 : 2; // [7:6] 230 | TCR_IRGN0 IRGN0 : 2; // [9:8] 231 | TCR_ORGN0 ORGN0 : 2; // [11:10] 232 | TCR_SH0 SH0 : 2; // [13:12] 233 | TCR_TG0 TG0 : 2; // [15:14] 234 | TCR_PS PS : 3; // [18:16] 235 | tcr_value_t RES0_2 : 1; // [19] 236 | TCR_TBI TBI : 1; // [20] 237 | tcr_value_t RES0_3 : 2; // [22:21] 238 | tcr_value_t RES1_1 : 1; // [23] 239 | tcr_value_t RES0_4 : 7; // [30:24] 240 | tcr_value_t RES1_2 : 1; // [31] 241 | } details; 242 | 243 | tcr_value_t value; 244 | 245 | TCRFormat(tcr_value_t value) : value(value) { } 246 | 247 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 248 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 249 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 250 | TCR_SH0 getSH0() { return details.SH0; } 251 | TCR_TG0 getTG0() { return details.TG0; } 252 | TCR_PS getPS() { return details.PS; } 253 | TCR_TBI getTBI() { return details.TBI; } 254 | }; 255 | 256 | // MARK: - TCR 257 | 258 | template 259 | class TCR 260 | { 261 | public: 262 | 263 | using tcr_value_t = typename TCRFormat::tcr_value_t; 264 | 265 | TCR() : m_tcr(0) 266 | {} 267 | 268 | TCR(tcr_value_t value) : m_tcr(value) 269 | {} 270 | 271 | ~TCR() 272 | {}; 273 | 274 | tcr_value_t getValue() { return m_tcr.value; } 275 | 276 | uint32_t getT0SZ() { return m_tcr.getT0SZ(); } 277 | TCR_EPD0 getEPD0() { return m_tcr.getEPD0(); } 278 | TCR_IRGN0 getIRGN0() { return m_tcr.getIRGN0(); } 279 | TCR_ORGN0 getORGN0() { return m_tcr.getORGN0(); } 280 | TCR_SH0 getSH0() { return m_tcr.getSH0(); } 281 | TCR_TG0 getTG0() { return m_tcr.getTG0(); } 282 | uint32_t getT1SZ() { return m_tcr.getT1SZ(); } 283 | TCR_A1 getA1() { return m_tcr.getA1(); } 284 | TCR_EPD1 getEPD1() { return m_tcr.getEPD1(); } 285 | TCR_IRGN1 getIRGN1() { return m_tcr.getIRGN1(); } 286 | TCR_ORGN1 getORGN1() { return m_tcr.getORGN1(); } 287 | TCR_SH1 getSH1() { return m_tcr.getSH1(); } 288 | TCR_TG1 getTG1() { return m_tcr.getTG1(); } 289 | TCR_IPS getIPS() { return m_tcr.getIPS(); } 290 | TCR_AS getAS() { return m_tcr.getAS(); } 291 | TCR_TBI getTBI0() { return m_tcr.getTBI0(); } 292 | TCR_TBI getTBI1() { return m_tcr.getTBI1(); } 293 | 294 | TCR_PS getPS() { return m_tcr.getPS(); } 295 | TCR_TBI getTBI() { return m_tcr.getTBI(); } 296 | 297 | private: 298 | 299 | using TCRFormatType = TCRFormat; 300 | 301 | TCRFormatType m_tcr; 302 | }; 303 | 304 | // MARK: - TCR types 305 | 306 | using TCR_EL1 = TCR; 307 | using TCR_EL2 = TCR; 308 | using TCR_EL3 = TCR; 309 | 310 | -------------------------------------------------------------------------------- /MMUit/VMAKit/TTEntry.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct 21 | { 22 | TTGranule granule; 23 | TTLevel level; 24 | ttentry_t descriptor; 25 | } TTEntryDetails; 26 | 27 | // Generic Entry 28 | static const ttentry_t kTTDescriptor_ValidBit = (1 << 0); 29 | static const ttentry_t kTTDescriptor_TableBit = (1 << 1); 30 | static const ttentry_t kTTDescriptor_PageBit = (1 << 1); 31 | 32 | // Table attributes 33 | static const ttentry_t kTTDescriptor_PXNTableBitShift = (59); 34 | static const ttentry_t kTTDescriptor_PXNTableBitMask = ((ttentry_t)1 << kTTDescriptor_PXNTableBitShift); 35 | static const ttentry_t kTTDescriptor_XNTableBitShift = (60); 36 | static const ttentry_t kTTDescriptor_XNTableBitMask = ((ttentry_t)1 << kTTDescriptor_XNTableBitShift); 37 | static const ttentry_t kTTDescriptor_APTableBitShift = (61); 38 | static const ttentry_t kTTDescriptor_APTableBitMask = ((ttentry_t)0b11 << kTTDescriptor_APTableBitShift); 39 | static const ttentry_t kTTDescriptor_NSTableBitShift = (63); 40 | static const ttentry_t kTTDescriptor_NSTableBitMask = ((ttentry_t)1 << kTTDescriptor_NSTableBitShift); 41 | 42 | typedef enum { 43 | kAPTable_NoEffect = 0b00, 44 | kAPTable_NoEL0ReadAccess = 0b01, 45 | kAPTable_NoAnyWriteAccess = 0b10, 46 | kAPTable_NoEL0ReadAndAnyWriteAccess = 0b11, 47 | } TTDescriptorAPTable; 48 | 49 | // Block and Page attributes 50 | 51 | static const ttentry_t kTTDescriptor_AttrIndxBitShift = (2); 52 | static const ttentry_t kTTDescriptor_AttrIndxBitMask = ((ttentry_t)0b111 << kTTDescriptor_AttrIndxBitShift); 53 | static const ttentry_t kTTDescriptor_NSBitShift = (5); 54 | static const ttentry_t kTTDescriptor_NSBitMask = ((ttentry_t)1 << kTTDescriptor_NSBitShift); 55 | static const ttentry_t kTTDescriptor_APBitShift = (6); 56 | static const ttentry_t kTTDescriptor_APBitMask = ((ttentry_t)0b11 << kTTDescriptor_APBitShift); 57 | static const ttentry_t kTTDescriptor_SHBitShift = (8); 58 | static const ttentry_t kTTDescriptor_SHBitMask = ((ttentry_t)0b11 << kTTDescriptor_SHBitShift); 59 | static const ttentry_t kTTDescriptor_AFBitShift = (10); 60 | static const ttentry_t kTTDescriptor_AFBitMask = ((ttentry_t)1 << kTTDescriptor_AFBitShift); 61 | static const ttentry_t kTTDescriptor_nGBitShift = (11); 62 | static const ttentry_t kTTDescriptor_nGBitMask = ((ttentry_t)1 << kTTDescriptor_nGBitShift); 63 | static const ttentry_t kTTDescriptor_ContiguousBitShift = (52); 64 | static const ttentry_t kTTDescriptor_ContiguousBitMask = ((ttentry_t)1 << kTTDescriptor_ContiguousBitShift); 65 | static const ttentry_t kTTDescriptor_PXNBitShift = (53); 66 | static const ttentry_t kTTDescriptor_PXNBitMask = ((ttentry_t)1 << kTTDescriptor_PXNBitShift); 67 | static const ttentry_t kTTDescriptor_XNBitShift = (54); 68 | static const ttentry_t kTTDescriptor_XNBitMask = ((ttentry_t)1 << kTTDescriptor_XNBitShift); 69 | 70 | typedef enum { 71 | kAP_HigherELReadWriteEL0None = 0b00, 72 | kAP_HigherELReadWriteEL0ReadWrite = 0b01, 73 | kAP_HigherELReadOnlyEL0None = 0b10, 74 | kAP_HigherELReadOnlyEL0ReadOnly = 0b11, 75 | 76 | } TTDescriptorAP; 77 | 78 | typedef enum { 79 | kSH_NonShareable = 0b00, 80 | kSH_Reserved = 0b01, 81 | kSH_OuterShareable = 0b10, 82 | kSH_InnerShareable = 0b11, 83 | } TTDescriptorSH; 84 | 85 | // Generic Entry 86 | 87 | bool ttentry_IsValid(TTEntryDetails* entry); 88 | bool ttentry_IsBlockDescriptor(TTEntryDetails* entry); 89 | bool ttentry_IsTableDescriptor(TTEntryDetails* entry); 90 | bool ttentry_IsReserved(TTEntryDetails* entry); 91 | bool ttentry_IsPageDescriptor(TTEntryDetails* entry); 92 | 93 | ttentry_t ttentry_GetDescriptor(TTEntryDetails* entry); 94 | void ttentry_SetDescriptor(TTEntryDetails* entry, ttentry_t descriptor); 95 | 96 | phys_addr_t ttentry_GetOutputAddress(TTEntryDetails* entry); 97 | void ttentry_SetOutputAddress(TTEntryDetails* entry, phys_addr_t address); 98 | 99 | // Table Entry 100 | 101 | bool ttentry_GetPXNTable(TTEntryDetails* entry); 102 | bool ttentry_GetXNTable(TTEntryDetails* entry); 103 | TTDescriptorAPTable ttentry_GetAPTable(TTEntryDetails* entry); 104 | bool ttentry_GetNSTable(TTEntryDetails* entry); 105 | 106 | void ttentry_SetPXNTable(TTEntryDetails* entry, bool value); 107 | void ttentry_SetXNTable(TTEntryDetails* entry, bool value); 108 | void ttentry_SetAPTable(TTEntryDetails* entry, TTDescriptorAPTable value); 109 | void ttentry_SetNSTable(TTEntryDetails* entry, bool value); 110 | 111 | // Block or Page Entry 112 | 113 | uint8_t ttentry_GetAttrIndx(TTEntryDetails* entry); 114 | bool ttentry_GetNS(TTEntryDetails* entry); 115 | TTDescriptorAP ttentry_GetAP(TTEntryDetails* entry); 116 | TTDescriptorSH ttentry_GetSH(TTEntryDetails* entry); 117 | bool ttentry_GetAF(TTEntryDetails* entry); 118 | bool ttentry_GetNG(TTEntryDetails* entry); 119 | bool ttentry_GetContiguous(TTEntryDetails* entry); 120 | bool ttentry_GetPXN(TTEntryDetails* entry); 121 | bool ttentry_GetXN(TTEntryDetails* entry); 122 | 123 | void ttentry_SetAttrIndx(TTEntryDetails* entry, uint8_t value); 124 | void ttentry_SetNS(TTEntryDetails* entry, bool value); 125 | void ttentry_SetAP(TTEntryDetails* entry, TTDescriptorAP value); 126 | void ttentry_SetSH(TTEntryDetails* entry, TTDescriptorSH value); 127 | void ttentry_SetAF(TTEntryDetails* entry, bool value); 128 | void ttentry_SetNG(TTEntryDetails* entry, bool value); 129 | void ttentry_SetContiguous(TTEntryDetails* entry, bool value); 130 | void ttentry_SetPXN(TTEntryDetails* entry, bool value); 131 | void ttentry_SetXN(TTEntryDetails* entry, bool value); 132 | 133 | #ifdef __cplusplus 134 | } 135 | #endif 136 | -------------------------------------------------------------------------------- /MMUit/VMAKit/TTWalker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TTEntry.h" 14 | #include "MMUConfig.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct { 21 | MMUConfig mmu_config; 22 | virt_addr_t table_base; 23 | 24 | uintptr_t cb_user_data; 25 | 26 | // primitives 27 | uintptr_t (*read_address)(virt_addr_t address); 28 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 29 | 30 | } ttwalker; 31 | 32 | #ifndef __cplusplus 33 | 34 | typedef enum { 35 | kWalkOperation_Stop = 0, 36 | kWalkOperation_Continue = 1 37 | } WalkOperation; 38 | 39 | typedef enum { 40 | kWalkResultType_Complete = 0, 41 | kWalkResultType_Stopped = 1, 42 | kWalkResultType_Failed = 2, 43 | kWalkResultType_Undefined 44 | } WalkResultType; 45 | 46 | typedef struct { 47 | TTLevel level; 48 | virt_addr_t tableAddress; 49 | offset_t entryOffset; 50 | } WalkPosition; 51 | 52 | typedef struct { 53 | WalkResultType type; 54 | TTLevel level; 55 | ttentry_t descriptor; 56 | phys_addr_t outputAddress; 57 | } WalkResult; 58 | 59 | #endif 60 | 61 | typedef WalkOperation (*ttwalker_callback)(WalkPosition* position, TTEntryDetails* entry, uintptr_t user_data); 62 | 63 | WalkResult ttwalker_Walk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback); 64 | bool ttwalker_ReverseWalk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback); 65 | phys_addr_t ttwalker_FindPhysicalAddress(ttwalker* walker, virt_addr_t address); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /MMUit/VMAKit/TTWalker.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "MMUConfig.hpp" 13 | #include 14 | 15 | enum class WalkOperation { 16 | Stop = false, 17 | Continue = true 18 | }; 19 | 20 | enum class WalkResultType { 21 | Complete = 0, 22 | Stopped = 1, 23 | Failed = 2, 24 | Undefined = 3 25 | }; 26 | 27 | struct WalkPosition { 28 | TTLevel level; 29 | virt_addr_t tableAddress; 30 | offset_t entryOffset; 31 | }; 32 | 33 | struct WalkResult { 34 | WalkResultType type; 35 | TTLevel level; 36 | ttentry_t descriptor; 37 | phys_addr_t outputAddress; 38 | 39 | WalkResultType getType() { return type; } 40 | TTLevel getLevel() { return level; } 41 | ttentry_t getDescriptor() { return descriptor; } 42 | phys_addr_t getOutputAddress() { return outputAddress; } 43 | 44 | WalkResult& setType(WalkResultType type) {this->type = type; return *this; } 45 | WalkResult& setLevel(TTLevel level) {this->level = level; return *this; } 46 | WalkResult& setDescriptor(ttentry_t descriptor) {this->descriptor = descriptor; return *this; } 47 | WalkResult& setOutputAddress(phys_addr_t address) {this->outputAddress = address; return *this; } 48 | }; 49 | 50 | class TTGenericWalker 51 | { 52 | public: 53 | 54 | // WalkerCallback callback is called for every level walker is going through 55 | using WalkerCallback = std::function; 56 | static const WalkerCallback DefaultCallback; 57 | 58 | public: 59 | 60 | virtual WalkResult walkTo(virt_addr_t address, WalkerCallback callback) = 0; 61 | virtual bool reverseWalkFrom(virt_addr_t address, WalkerCallback callback = DefaultCallback) = 0; 62 | virtual phys_addr_t findPhysicalAddress(virt_addr_t address) = 0; 63 | 64 | }; 65 | 66 | const TTGenericWalker::WalkerCallback TTGenericWalker::DefaultCallback = 67 | [] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation 68 | { 69 | return WalkOperation::Continue; 70 | }; 71 | 72 | template 73 | class TTWalker : public PRIMITIVES, public TTGenericWalker 74 | { 75 | public: 76 | 77 | TTWalker() = delete; 78 | 79 | TTWalker(MMUConfig mmuConfig, virt_addr_t tableBase) 80 | : m_mmuConfig(mmuConfig), m_tableBase(tableBase) 81 | {} 82 | 83 | WalkResult walkTo(virt_addr_t address, WalkerCallback callback = DefaultCallback) override 84 | { 85 | switch (m_mmuConfig.granule) { 86 | case TTGranule::Granule4K: return performWalkTo(address, callback); 87 | case TTGranule::Granule16K: return performWalkTo(address, callback); 88 | case TTGranule::Granule64K: return performWalkTo(address, callback); 89 | 90 | default: assert(0); 91 | } 92 | 93 | return WalkResult(); 94 | } 95 | 96 | bool reverseWalkFrom(virt_addr_t address, WalkerCallback callback) override 97 | { 98 | switch (m_mmuConfig.granule) { 99 | case TTGranule::Granule4K: return performReverseWalkFrom(address, callback); 100 | case TTGranule::Granule16K: return performReverseWalkFrom(address, callback); 101 | case TTGranule::Granule64K: return performReverseWalkFrom(address, callback); 102 | 103 | default: assert(0); 104 | } 105 | 106 | return false; 107 | } 108 | 109 | phys_addr_t findPhysicalAddress(virt_addr_t address) override 110 | { 111 | virt_addr_t pageMask = uint32_t(m_mmuConfig.granule) - 1; 112 | 113 | auto result = walkTo(address); 114 | if (result.getType() == WalkResultType::Complete) 115 | return result.getOutputAddress() | (address & pageMask); 116 | else 117 | return kInvalidAddress; 118 | } 119 | 120 | private: 121 | 122 | template 123 | WalkResult performWalkTo(virt_addr_t address, WalkerCallback callback = DefaultCallback) 124 | { 125 | WalkResult result; 126 | WalkPosition pos = { 127 | .level = m_mmuConfig.initialLevel, 128 | .tableAddress = m_tableBase, 129 | .entryOffset = 0 130 | }; 131 | 132 | VirtualAddress va(address, m_mmuConfig.regionSizeOffset); 133 | 134 | while (1) 135 | { 136 | result.level = pos.level; 137 | result.descriptor = 0; 138 | 139 | pos.entryOffset = va.getOffsetForLevel(pos.level); 140 | 141 | // get current table translation entry 142 | switch (pos.level) 143 | { 144 | case TTLevel::Level0: 145 | { 146 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 147 | result.descriptor = entry.getDescriptor(); 148 | 149 | // check is entry is valid 150 | if (entry.isValid() == false) 151 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 152 | 153 | // invalid if not table descriptor 154 | if (entry.isTableDescriptor() == false) 155 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 156 | 157 | // execute callback and interrupt walk if needed 158 | if (callback(&pos, &entry) == WalkOperation::Stop) 159 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 160 | 161 | // get next table address 162 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 163 | 164 | break; 165 | } 166 | case TTLevel::Level1: 167 | { 168 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 169 | result.descriptor = entry.getDescriptor(); 170 | 171 | // check is entry is valid 172 | if (entry.isValid() == false) 173 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 174 | 175 | // execute callback and interrupt walk if needed 176 | if (callback(&pos, &entry) == WalkOperation::Stop) 177 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 178 | 179 | // return block address if not table descriptor 180 | if (entry.isTableDescriptor() == false) 181 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 182 | 183 | // get next table address 184 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 185 | 186 | break; 187 | } 188 | case TTLevel::Level2: 189 | { 190 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 191 | result.descriptor = entry.getDescriptor(); 192 | 193 | // check is entry is valid 194 | if (entry.isValid() == false) 195 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 196 | 197 | // execute callback and interrupt walk if needed 198 | if (callback(&pos, &entry) == WalkOperation::Stop) 199 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 200 | 201 | // return block address if not table descriptor 202 | if (entry.isTableDescriptor() == false) 203 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 204 | 205 | // get next table address 206 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 207 | 208 | break; 209 | } 210 | case TTLevel::Level3: 211 | { 212 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 213 | result.descriptor = entry.getDescriptor(); 214 | 215 | // check is entry is valid 216 | if (entry.isValid() == false) 217 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 218 | 219 | // invalid if not page descriptor 220 | if (entry.isPageDescriptor() == false) 221 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 222 | 223 | // execute callback and interrupt walk if needed 224 | if (callback(&pos, &entry) == WalkOperation::Stop) 225 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 226 | 227 | // return page address 228 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 229 | } 230 | default: assert(0); 231 | } 232 | 233 | if (pos.tableAddress == kInvalidAddress) 234 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 235 | 236 | // switch to the next level 237 | pos.level++; 238 | } 239 | 240 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 241 | } 242 | 243 | template 244 | bool performReverseWalkFrom(virt_addr_t address, WalkerCallback callback) 245 | { 246 | struct ReverseWalk 247 | { 248 | uint32_t levels; 249 | WalkPosition position[uint32_t(TTLevel::Count)]; 250 | ttentry_t entry[uint32_t(TTLevel::Count)]; 251 | } walk; 252 | 253 | walk.levels = 0; 254 | 255 | // walk forward and save translation lookups 256 | TTWalker walker(m_mmuConfig, m_tableBase); 257 | WalkResult result = walker.walkTo(address, [&walk] (WalkPosition* position, TTGenericEntry* entry) { 258 | // safety checks 259 | if (position == nullptr || entry == nullptr) 260 | return WalkOperation::Stop; 261 | 262 | walk.position[walk.levels] = *position; 263 | walk.entry[walk.levels] = entry->getDescriptor(); 264 | walk.levels++; 265 | 266 | return WalkOperation::Continue; 267 | }); 268 | 269 | if (result.getType() == WalkResultType::Failed) 270 | return false; 271 | 272 | // walk backwards 273 | while (walk.levels != 0) 274 | { 275 | uint32_t currentLevel = walk.levels - 1; 276 | 277 | switch (walk.position[currentLevel].level) 278 | { 279 | case TTLevel::Level0: 280 | { 281 | auto entry = TTEntry(walk.entry[currentLevel]); 282 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 283 | return false; 284 | break; 285 | } 286 | case TTLevel::Level1: 287 | { 288 | auto entry = TTEntry(walk.entry[currentLevel]); 289 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 290 | return false; 291 | break; 292 | } 293 | case TTLevel::Level2: 294 | { 295 | auto entry = TTEntry(walk.entry[currentLevel]); 296 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 297 | return false; 298 | break; 299 | } 300 | case TTLevel::Level3: 301 | { 302 | auto entry = TTEntry(walk.entry[currentLevel]); 303 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 304 | return false; 305 | break; 306 | } 307 | default: assert(0); 308 | } 309 | 310 | walk.levels--; 311 | } 312 | 313 | return true; 314 | } 315 | 316 | private: 317 | 318 | MMUConfig m_mmuConfig; 319 | virt_addr_t m_tableBase = kInvalidAddress; 320 | }; 321 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VMAKit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #include "VMAKit.hpp" 10 | #include "Primitives.hpp" 11 | 12 | 13 | #include "TTWalker.h" 14 | #include "PageRelocator.h" 15 | 16 | class ttwalkerPrimitives : public Primitives 17 | { 18 | public: 19 | 20 | ttwalkerPrimitives() 21 | { 22 | if (s_walker) 23 | { 24 | m_funcReadAddress = s_walker->read_address; 25 | m_funcPhysicalToVirtual = s_walker->physical_to_virtual; 26 | } 27 | } 28 | 29 | static void init(ttwalker* walker) 30 | { 31 | if (walker == nullptr) 32 | return; 33 | 34 | s_walker = walker; 35 | } 36 | 37 | static void close() 38 | { 39 | s_walker = nullptr; 40 | } 41 | 42 | uintptr_t readAddress(uintptr_t address) 43 | { 44 | if (m_funcReadAddress == nullptr) 45 | return kInvalidAddress; 46 | 47 | return m_funcReadAddress(address); 48 | }; 49 | 50 | virt_addr_t physicalToVirtual(phys_addr_t address) 51 | { 52 | if (m_funcPhysicalToVirtual == nullptr) 53 | return kInvalidAddress; 54 | 55 | return m_funcPhysicalToVirtual(address); 56 | } 57 | 58 | private: 59 | 60 | static ttwalker* s_walker; 61 | 62 | uintptr_t (*m_funcReadAddress)(virt_addr_t address) = nullptr; 63 | virt_addr_t (*m_funcPhysicalToVirtual)(phys_addr_t address) = nullptr; 64 | 65 | }; 66 | 67 | ttwalker* ttwalkerPrimitives::s_walker = nullptr; 68 | 69 | class pagerelocatorPrimitives : public Primitives 70 | { 71 | public: 72 | 73 | pagerelocatorPrimitives() 74 | { 75 | if (s_relocator) 76 | { 77 | m_funcReadAddress = s_relocator->read_address; 78 | m_funcWriteAddress = s_relocator->write_address; 79 | 80 | m_funcCopyInKernel = s_relocator->copy_in_kernel; 81 | 82 | m_funcAllocInPhysicalMemory = s_relocator->alloc_in_physical_memory; 83 | m_funcDeallocInPhysicalMemory = s_relocator->dealloc_in_physical_memory; 84 | 85 | m_funcPhysicalToVirtual = s_relocator->physical_to_virtual; 86 | m_funcVirtualToPhysical = s_relocator->virtual_to_physical; 87 | } 88 | } 89 | 90 | static void init(pagerelocator* relocator) 91 | { 92 | if (relocator == nullptr) 93 | return; 94 | 95 | s_relocator = relocator; 96 | } 97 | 98 | static void close() 99 | { 100 | s_relocator = nullptr; 101 | } 102 | 103 | uintptr_t readAddress(virt_addr_t address) 104 | { 105 | if (m_funcReadAddress == nullptr) 106 | return kInvalidAddress; 107 | 108 | return m_funcReadAddress(address); 109 | } 110 | void writeAddress(virt_addr_t address, virt_addr_t data) 111 | { 112 | if (m_funcWriteAddress != nullptr) 113 | m_funcWriteAddress(address, data); 114 | } 115 | 116 | void copyInKernel(virt_addr_t dst, virt_addr_t src, uint32_t size) 117 | { 118 | if (m_funcCopyInKernel != nullptr) 119 | m_funcCopyInKernel(dst, src, size); 120 | } 121 | 122 | virt_addr_t allocInPhysicalMemory(uint32_t size) 123 | { 124 | if (m_funcAllocInPhysicalMemory == nullptr) 125 | return kInvalidAddress; 126 | 127 | return m_funcAllocInPhysicalMemory(size); 128 | } 129 | bool deallocInPhysicalMemory(virt_addr_t address, uint32_t size) 130 | { 131 | if (m_funcDeallocInPhysicalMemory == nullptr) 132 | return kInvalidAddress; 133 | 134 | return m_funcDeallocInPhysicalMemory(address, size); 135 | } 136 | 137 | virt_addr_t physicalToVirtual(phys_addr_t address) 138 | { 139 | if (m_funcPhysicalToVirtual == nullptr) 140 | return kInvalidAddress; 141 | 142 | return m_funcPhysicalToVirtual(address); 143 | } 144 | virt_addr_t virtualToPhysical(phys_addr_t address) 145 | { 146 | if (m_funcVirtualToPhysical == nullptr) 147 | return kInvalidAddress; 148 | 149 | return m_funcVirtualToPhysical(address); 150 | } 151 | 152 | private: 153 | 154 | static pagerelocator* s_relocator; 155 | 156 | uintptr_t (*m_funcReadAddress)(virt_addr_t address) = nullptr; 157 | void (*m_funcWriteAddress)(virt_addr_t address, virt_addr_t data) = nullptr; 158 | 159 | void (*m_funcCopyInKernel)(virt_addr_t dst, virt_addr_t src, uint32_t size) = nullptr; 160 | 161 | virt_addr_t (*m_funcAllocInPhysicalMemory)(uint32_t size) = nullptr; 162 | bool (*m_funcDeallocInPhysicalMemory)(virt_addr_t address, uint32_t size) = nullptr; 163 | 164 | virt_addr_t (*m_funcPhysicalToVirtual)(phys_addr_t address) = nullptr; 165 | virt_addr_t (*m_funcVirtualToPhysical)(phys_addr_t address) = nullptr; 166 | 167 | }; 168 | 169 | pagerelocator* pagerelocatorPrimitives::s_relocator = nullptr; 170 | 171 | static TTLevel0Entry_4K gLO_4K(kTTDescriptor_TableBit); // must be table 172 | static TTLevel1Entry_4K gL1_4K; 173 | static TTLevel2Entry_4K gL2_4K; 174 | static TTLevel3Entry_4K gL3_4K(kTTDescriptor_PageBit); // must be page; 175 | 176 | static TTLevel0Entry_16K gLO_16K(kTTDescriptor_TableBit); // must be table 177 | static TTLevel1Entry_16K gL1_16K(kTTDescriptor_TableBit); // must be table 178 | static TTLevel2Entry_16K gL2_16K; 179 | static TTLevel3Entry_16K gL3_16K(kTTDescriptor_PageBit); // must be page; 180 | 181 | static TTLevel0Entry_64K gLO_64K(kTTDescriptor_TableBit); // must be table 182 | static TTLevel1Entry_64K gL1_64K(kTTDescriptor_TableBit); // must be table 183 | static TTLevel2Entry_64K gL2_64K; 184 | static TTLevel3Entry_64K gL3_64K(kTTDescriptor_PageBit); // must be page; 185 | 186 | static TTGenericEntry* gTTEntryObjects[uint32_t(TTGranule::Count)][uint32_t(TTLevel::Count)] = { 187 | { &gLO_4K, &gL1_4K, &gL2_4K, &gL3_4K }, 188 | { &gLO_16K, &gL1_16K, &gL2_16K, &gL3_16K }, 189 | { &gLO_64K, &gL1_64K, &gL2_64K, &gL3_64K }, 190 | }; 191 | 192 | TTGenericEntry* GetTTEntryObject(TTEntryDetails* entry) 193 | { 194 | if (! entry) return nullptr; 195 | 196 | uint32_t granuleInx; 197 | switch (entry->granule) 198 | { 199 | case TTGranule::Granule4K: granuleInx = 0; break; 200 | case TTGranule::Granule16K: granuleInx = 1; break; 201 | case TTGranule::Granule64K: granuleInx = 2; break; 202 | default: assert(0); 203 | } 204 | uint32_t levelIdx = uint32_t(entry->level); 205 | 206 | auto entryObj = gTTEntryObjects[granuleInx][levelIdx]; 207 | entryObj->setDescriptor(entry->descriptor); 208 | 209 | return entryObj; 210 | } 211 | 212 | extern "C" 213 | { 214 | // MARK: - ttentry functions (Generic Entry) 215 | 216 | bool ttentry_IsValid(TTEntryDetails* entry) 217 | { 218 | auto entryObj = GetTTEntryObject(entry); 219 | if (entryObj == nullptr) 220 | assert(0); 221 | 222 | return entryObj->isValid(); 223 | } 224 | 225 | bool ttentry_IsBlockDescriptor(TTEntryDetails* entry) 226 | { 227 | auto entryObj = GetTTEntryObject(entry); 228 | if (entryObj == nullptr) 229 | assert(0); 230 | 231 | return entryObj->isBlockDescriptor(); 232 | } 233 | 234 | bool ttentry_IsTableDescriptor(TTEntryDetails* entry) 235 | { 236 | auto entryObj = GetTTEntryObject(entry); 237 | if (entryObj == nullptr) 238 | assert(0); 239 | 240 | return entryObj->isTableDescriptor(); 241 | } 242 | 243 | bool ttentry_IsReserved(TTEntryDetails* entry) 244 | { 245 | auto entryObj = GetTTEntryObject(entry); 246 | if (entryObj == nullptr) 247 | assert(0); 248 | 249 | return entryObj->isReserved(); 250 | } 251 | 252 | bool ttentry_IsPageDescriptor(TTEntryDetails* entry) 253 | { 254 | auto entryObj = GetTTEntryObject(entry); 255 | if (entryObj == nullptr) 256 | assert(0); 257 | 258 | return entryObj->isPageDescriptor(); 259 | } 260 | 261 | ttentry_t ttentry_GetDescriptor(TTEntryDetails* entry) 262 | { 263 | if (entry == nullptr) 264 | assert(0); 265 | 266 | return entry->descriptor; 267 | } 268 | 269 | void ttentry_SetDescriptor(TTEntryDetails* entry, ttentry_t descriptor) 270 | { 271 | if (entry == nullptr) 272 | assert(0); 273 | 274 | entry->descriptor = descriptor; 275 | } 276 | 277 | phys_addr_t ttentry_GetOutputAddress(TTEntryDetails* entry) 278 | { 279 | auto entryObj = GetTTEntryObject(entry); 280 | if (entryObj == nullptr) 281 | assert(0); 282 | 283 | return entryObj->getOutputAddress(); 284 | } 285 | 286 | void ttentry_SetOutputAddress(TTEntryDetails* entry, phys_addr_t address) 287 | { 288 | auto entryObj = GetTTEntryObject(entry); 289 | if (entryObj == nullptr) 290 | assert(0); 291 | 292 | entryObj->setOutputAddress(address); 293 | entry->descriptor = entryObj->getDescriptor(); 294 | } 295 | 296 | // MARK: - ttentry functions (Table Entry) 297 | 298 | bool ttentry_GetPXNTable(TTEntryDetails* entry) 299 | { 300 | auto entryObj = GetTTEntryObject(entry); 301 | if (entryObj == nullptr) 302 | assert(0); 303 | 304 | if (entryObj->isTableDescriptor() == false) 305 | assert(0); 306 | 307 | return (entryObj->getDescriptor() & kTTDescriptor_PXNTableBitMask) != 0; 308 | } 309 | 310 | bool ttentry_GetXNTable(TTEntryDetails* entry) 311 | { 312 | auto entryObj = GetTTEntryObject(entry); 313 | if (entryObj == nullptr) 314 | assert(0); 315 | 316 | if (entryObj->isTableDescriptor() == false) 317 | assert(0); 318 | 319 | return (entryObj->getDescriptor() & kTTDescriptor_XNTableBitMask) != 0; 320 | } 321 | 322 | TTDescriptorAPTable ttentry_GetAPTable(TTEntryDetails* entry) 323 | { 324 | auto entryObj = GetTTEntryObject(entry); 325 | if (entryObj == nullptr) 326 | assert(0); 327 | 328 | if (entryObj->isTableDescriptor() == false) 329 | assert(0); 330 | 331 | return TTDescriptorAPTable((entryObj->getDescriptor() & kTTDescriptor_APTableBitMask) >> kTTDescriptor_APTableBitShift); 332 | } 333 | 334 | bool ttentry_GetNSTable(TTEntryDetails* entry) 335 | { 336 | auto entryObj = GetTTEntryObject(entry); 337 | if (entryObj == nullptr) 338 | assert(0); 339 | 340 | if (entryObj->isTableDescriptor() == false) 341 | assert(0); 342 | 343 | return (entryObj->getDescriptor() & kTTDescriptor_NSTableBitMask) != 0; 344 | } 345 | 346 | void ttentry_SetPXNTable(TTEntryDetails* entry, bool value) 347 | { 348 | auto entryObj = GetTTEntryObject(entry); 349 | if (entryObj == nullptr) 350 | assert(0); 351 | 352 | if (entryObj->isTableDescriptor() == false) 353 | assert(0); 354 | 355 | if (value) 356 | entry->descriptor |= kTTDescriptor_PXNTableBitMask; 357 | else 358 | entry->descriptor &= ~kTTDescriptor_PXNTableBitMask; 359 | } 360 | 361 | void ttentry_SetXNTable(TTEntryDetails* entry, bool value) 362 | { 363 | auto entryObj = GetTTEntryObject(entry); 364 | if (entryObj == nullptr) 365 | assert(0); 366 | 367 | if (entryObj->isTableDescriptor() == false) 368 | assert(0); 369 | 370 | if (value) 371 | entry->descriptor |= kTTDescriptor_XNTableBitMask; 372 | else 373 | entry->descriptor &= ~kTTDescriptor_XNTableBitMask; 374 | } 375 | 376 | void ttentry_SetAPTable(TTEntryDetails* entry, TTDescriptorAPTable value) 377 | { 378 | auto entryObj = GetTTEntryObject(entry); 379 | if (entryObj == nullptr) 380 | assert(0); 381 | 382 | if (entryObj->isTableDescriptor() == false) 383 | assert(0); 384 | 385 | entry->descriptor &= ~kTTDescriptor_PXNTableBitMask; 386 | entry->descriptor |= (ttentry_t(value) << kTTDescriptor_PXNTableBitShift); 387 | } 388 | 389 | void ttentry_SetNSTable(TTEntryDetails* entry, bool value) 390 | { 391 | auto entryObj = GetTTEntryObject(entry); 392 | if (entryObj == nullptr) 393 | assert(0); 394 | 395 | if (entryObj->isTableDescriptor() == false) 396 | assert(0); 397 | 398 | if (value) 399 | entry->descriptor |= kTTDescriptor_NSTableBitMask; 400 | else 401 | entry->descriptor &= ~kTTDescriptor_NSTableBitMask; 402 | } 403 | 404 | // MARK: - ttentry functions (Block or Page Entry) 405 | 406 | uint8_t ttentry_GetAttrIndx(TTEntryDetails* entry) 407 | { 408 | auto entryObj = GetTTEntryObject(entry); 409 | if (entryObj == nullptr) 410 | assert(0); 411 | 412 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 413 | assert(0); 414 | 415 | return uint8_t((entryObj->getDescriptor() & kTTDescriptor_AttrIndxBitMask) >> kTTDescriptor_AttrIndxBitShift); 416 | } 417 | 418 | bool ttentry_GetNS(TTEntryDetails* entry) 419 | { 420 | auto entryObj = GetTTEntryObject(entry); 421 | if (entryObj == nullptr) 422 | assert(0); 423 | 424 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 425 | assert(0); 426 | 427 | return (entryObj->getDescriptor() & kTTDescriptor_NSBitMask) != 0; 428 | } 429 | 430 | TTDescriptorAP ttentry_GetAP(TTEntryDetails* entry) 431 | { 432 | auto entryObj = GetTTEntryObject(entry); 433 | if (entryObj == nullptr) 434 | assert(0); 435 | 436 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 437 | assert(0); 438 | 439 | return TTDescriptorAP((entryObj->getDescriptor() & kTTDescriptor_APBitMask) >> kTTDescriptor_APBitShift); 440 | } 441 | 442 | TTDescriptorSH ttentry_GetSH(TTEntryDetails* entry) 443 | { 444 | auto entryObj = GetTTEntryObject(entry); 445 | if (entryObj == nullptr) 446 | assert(0); 447 | 448 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 449 | assert(0); 450 | 451 | return TTDescriptorSH((entryObj->getDescriptor() & kTTDescriptor_SHBitMask) >> kTTDescriptor_SHBitShift); 452 | } 453 | 454 | bool ttentry_GetAF(TTEntryDetails* entry) 455 | { 456 | auto entryObj = GetTTEntryObject(entry); 457 | if (entryObj == nullptr) 458 | assert(0); 459 | 460 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 461 | assert(0); 462 | 463 | return (entryObj->getDescriptor() & kTTDescriptor_AFBitMask) != 0; 464 | } 465 | 466 | bool ttentry_GetNG(TTEntryDetails* entry) 467 | { 468 | auto entryObj = GetTTEntryObject(entry); 469 | if (entryObj == nullptr) 470 | assert(0); 471 | 472 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 473 | assert(0); 474 | 475 | return (entryObj->getDescriptor() & kTTDescriptor_nGBitMask) != 0; 476 | } 477 | 478 | bool ttentry_GetContiguous(TTEntryDetails* entry) 479 | { 480 | auto entryObj = GetTTEntryObject(entry); 481 | if (entryObj == nullptr) 482 | assert(0); 483 | 484 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 485 | assert(0); 486 | 487 | return (entryObj->getDescriptor() & kTTDescriptor_ContiguousBitMask) != 0; 488 | } 489 | 490 | bool ttentry_GetPXN(TTEntryDetails* entry) 491 | { 492 | auto entryObj = GetTTEntryObject(entry); 493 | if (entryObj == nullptr) 494 | assert(0); 495 | 496 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 497 | assert(0); 498 | 499 | return (entryObj->getDescriptor() & kTTDescriptor_PXNBitMask) != 0; 500 | } 501 | 502 | bool ttentry_GetXN(TTEntryDetails* entry) 503 | { 504 | auto entryObj = GetTTEntryObject(entry); 505 | if (entryObj == nullptr) 506 | assert(0); 507 | 508 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 509 | assert(0); 510 | 511 | return (entryObj->getDescriptor() & kTTDescriptor_XNBitMask) != 0; 512 | } 513 | 514 | 515 | void ttentry_SetAttrIndx(TTEntryDetails* entry, uint8_t value) 516 | { 517 | auto entryObj = GetTTEntryObject(entry); 518 | if (entryObj == nullptr) 519 | assert(0); 520 | 521 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 522 | assert(0); 523 | 524 | entry->descriptor &= ~kTTDescriptor_AttrIndxBitMask; 525 | entry->descriptor |= (ttentry_t(value) << kTTDescriptor_AttrIndxBitShift); 526 | } 527 | 528 | void ttentry_SetNS(TTEntryDetails* entry, bool value) 529 | { 530 | auto entryObj = GetTTEntryObject(entry); 531 | if (entryObj == nullptr) 532 | assert(0); 533 | 534 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 535 | assert(0); 536 | 537 | if (value) 538 | entry->descriptor |= kTTDescriptor_NSBitMask; 539 | else 540 | entry->descriptor &= ~kTTDescriptor_NSBitMask; 541 | } 542 | 543 | void ttentry_SetAP(TTEntryDetails* entry, TTDescriptorAP value) 544 | { 545 | auto entryObj = GetTTEntryObject(entry); 546 | if (entryObj == nullptr) 547 | assert(0); 548 | 549 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 550 | assert(0); 551 | 552 | entry->descriptor &= ~kTTDescriptor_APBitMask; 553 | entry->descriptor |= (ttentry_t(value) << kTTDescriptor_APBitShift); 554 | } 555 | 556 | void ttentry_SetSH(TTEntryDetails* entry, TTDescriptorSH value) 557 | { 558 | auto entryObj = GetTTEntryObject(entry); 559 | if (entryObj == nullptr) 560 | assert(0); 561 | 562 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 563 | assert(0); 564 | 565 | entry->descriptor &= ~kTTDescriptor_SHBitMask; 566 | entry->descriptor |= (ttentry_t(value) << kTTDescriptor_SHBitShift); 567 | } 568 | 569 | void ttentry_SetAF(TTEntryDetails* entry, bool value) 570 | { 571 | auto entryObj = GetTTEntryObject(entry); 572 | if (entryObj == nullptr) 573 | assert(0); 574 | 575 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 576 | assert(0); 577 | 578 | if (value) 579 | entry->descriptor |= kTTDescriptor_AFBitMask; 580 | else 581 | entry->descriptor &= ~kTTDescriptor_AFBitMask; 582 | } 583 | 584 | void ttentry_SetNG(TTEntryDetails* entry, bool value) 585 | { 586 | auto entryObj = GetTTEntryObject(entry); 587 | if (entryObj == nullptr) 588 | assert(0); 589 | 590 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 591 | assert(0); 592 | 593 | if (value) 594 | entry->descriptor |= kTTDescriptor_nGBitMask; 595 | else 596 | entry->descriptor &= ~kTTDescriptor_nGBitMask; 597 | } 598 | 599 | void ttentry_SetContiguous(TTEntryDetails* entry, bool value) 600 | { 601 | auto entryObj = GetTTEntryObject(entry); 602 | if (entryObj == nullptr) 603 | assert(0); 604 | 605 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 606 | assert(0); 607 | 608 | if (value) 609 | entry->descriptor |= kTTDescriptor_ContiguousBitMask; 610 | else 611 | entry->descriptor &= ~kTTDescriptor_ContiguousBitMask; 612 | } 613 | 614 | void ttentry_SetPXN(TTEntryDetails* entry, bool value) 615 | { 616 | auto entryObj = GetTTEntryObject(entry); 617 | if (entryObj == nullptr) 618 | assert(0); 619 | 620 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 621 | assert(0); 622 | 623 | if (value) 624 | entry->descriptor |= kTTDescriptor_PXNBitMask; 625 | else 626 | entry->descriptor &= ~kTTDescriptor_PXNBitMask; 627 | } 628 | 629 | void ttentry_SetXN(TTEntryDetails* entry, bool value) 630 | { 631 | auto entryObj = GetTTEntryObject(entry); 632 | if (entryObj == nullptr) 633 | assert(0); 634 | 635 | if (entryObj->isBlockDescriptor() == false && entryObj->isPageDescriptor() == false) 636 | assert(0); 637 | 638 | if (value) 639 | entry->descriptor |= kTTDescriptor_XNBitMask; 640 | else 641 | entry->descriptor &= ~kTTDescriptor_XNBitMask; 642 | } 643 | 644 | // MARK: - MMU Config 645 | 646 | void mmuconfig_Init(mmuconfigparser* configparser) 647 | { 648 | if (configparser == nullptr) 649 | return; 650 | 651 | auto configParserObj = new MMUConfigParser(); 652 | configparser->object = (void*)configParserObj; 653 | } 654 | 655 | void mmuconfig_SetTCR_EL1(mmuconfigparser* configparser, tcr_el1_t tcr_value) 656 | { 657 | if (configparser == nullptr) 658 | return; 659 | 660 | auto configParserObj = (MMUConfigParser*)configparser->object; 661 | configParserObj->setTCR_EL1(tcr_value); 662 | } 663 | 664 | MMUConfig mmuconfig_GetConfigFor(mmuconfigparser* configparser, ExceptionLevel el) 665 | { 666 | if (configparser == nullptr) 667 | return MMUConfig(); 668 | 669 | auto configParserObj = (MMUConfigParser*)configparser->object; 670 | return configParserObj->getConfigFor(el); 671 | } 672 | 673 | void mmuconfig_Clear(mmuconfigparser* configparser) 674 | { 675 | if (configparser == nullptr) 676 | return; 677 | 678 | auto configParserObj = (MMUConfigParser*)configparser->object; 679 | configParserObj->clear(); 680 | } 681 | 682 | void mmuconfig_Close(mmuconfigparser* configparser) 683 | { 684 | if (configparser == nullptr) 685 | return; 686 | 687 | auto configParserObj = (MMUConfigParser*)configparser->object; 688 | delete configParserObj; 689 | configparser->object = nullptr; 690 | } 691 | 692 | // MARK: - ttwalker functions 693 | 694 | WalkResult ttwalker_Walk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback) 695 | { 696 | WalkResult result = {.type = WalkResultType::Undefined, .level = TTLevel::Level0, .descriptor = 0, .outputAddress = 0}; 697 | 698 | if (walker == nullptr) 699 | return result.setType(WalkResultType::Failed); 700 | 701 | ttwalkerPrimitives::init(walker); 702 | 703 | TTWalker walkerObj(walker->mmu_config, walker->table_base); 704 | result = walkerObj.walkTo(address, [callback, walker] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation { 705 | assert(position != nullptr && entry != nullptr); 706 | TTEntryDetails details = { 707 | .granule = walker->mmu_config.granule, 708 | .level = position->level, 709 | .descriptor = entry->getDescriptor() 710 | }; 711 | if (callback != nullptr) 712 | return callback(position, &details, walker->cb_user_data); 713 | else 714 | return WalkOperation::Continue; 715 | }); 716 | 717 | ttwalkerPrimitives::close(); 718 | 719 | return result; 720 | } 721 | 722 | bool ttwalker_ReverseWalk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback) 723 | { 724 | bool result = false; 725 | 726 | if (walker == nullptr) 727 | return false; 728 | 729 | ttwalkerPrimitives::init(walker); 730 | 731 | TTWalker walkerObj(walker->mmu_config, walker->table_base); 732 | result = walkerObj.reverseWalkFrom(address, [callback, walker] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation { 733 | assert(position != nullptr && entry != nullptr); 734 | TTEntryDetails details = { 735 | .granule = walker->mmu_config.granule, 736 | .level = position->level, 737 | .descriptor = entry->getDescriptor() 738 | }; 739 | if (callback != nullptr) 740 | return callback(position, &details, walker->cb_user_data); 741 | else 742 | return WalkOperation::Continue; 743 | }); 744 | 745 | ttwalkerPrimitives::close(); 746 | 747 | return result; 748 | } 749 | 750 | phys_addr_t ttwalker_FindPhysicalAddress(ttwalker* walker, virt_addr_t address) 751 | { 752 | if (walker == nullptr) 753 | return kInvalidAddress; 754 | 755 | ttwalkerPrimitives::init(walker); 756 | 757 | TTWalker walkerObj(walker->mmu_config, walker->table_base); 758 | 759 | phys_addr_t result = walkerObj.findPhysicalAddress(address); 760 | 761 | ttwalkerPrimitives::close(); 762 | 763 | return result; 764 | } 765 | 766 | // MARK: - pagerelocator functions 767 | 768 | void pagerelocator_Init(pagerelocator* relocator) 769 | { 770 | if (relocator == nullptr) 771 | return; 772 | 773 | pagerelocatorPrimitives::init(relocator); 774 | 775 | auto relocatorObj = new PageRelocator(relocator->mmu_config, relocator->table_base); 776 | relocator->object = (void*)relocatorObj; 777 | } 778 | 779 | bool pagerelocator_RelocatePage(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback) 780 | { 781 | if (relocator == nullptr) 782 | return false; 783 | 784 | if (relocator->object == nullptr) 785 | return false; 786 | 787 | virt_addr_t page = pagerelocator_PreparePageRelocation(relocator, address, callback); 788 | if (page == kInvalidAddress) 789 | return false; 790 | 791 | return pagerelocator_CompleteRelocation(relocator); 792 | } 793 | 794 | virt_addr_t pagerelocator_PreparePageRelocation(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback) 795 | { 796 | if (relocator == nullptr) 797 | return false; 798 | 799 | if (relocator->object == nullptr) 800 | return false; 801 | 802 | auto relocatorObj = (PageRelocator*)relocator->object; 803 | return relocatorObj->preparePageRelocationFor(address, [callback, relocator] (TTLevel level, 804 | TTGenericEntry* oldEntry, 805 | TTGenericEntry* newEntry) -> ttentry_t { 806 | assert(oldEntry != nullptr && newEntry != nullptr); 807 | TTEntryDetails oldDetails = { 808 | .granule = relocator->mmu_config.granule, 809 | .level = level, 810 | .descriptor = oldEntry->getDescriptor() 811 | }; 812 | TTEntryDetails newDetails = { 813 | .granule = relocator->mmu_config.granule, 814 | .level = level, 815 | .descriptor = newEntry->getDescriptor() 816 | }; 817 | if (callback != nullptr) 818 | return callback(level, &oldDetails, &newDetails, relocator->cb_user_data); 819 | else 820 | return newDetails.descriptor; 821 | }); 822 | } 823 | 824 | bool pagerelocator_CompleteRelocation(pagerelocator* relocator) 825 | { 826 | if (relocator == nullptr) 827 | return false; 828 | 829 | if (relocator->object == nullptr) 830 | return false; 831 | 832 | auto relocatorObj = (PageRelocator*)relocator->object; 833 | return relocatorObj->completeRelocation(); 834 | } 835 | 836 | bool pagerelocator_CancelRelocation(pagerelocator* relocator) 837 | { 838 | if (relocator == nullptr) 839 | return false; 840 | 841 | if (relocator->object == nullptr) 842 | return false; 843 | 844 | auto relocatorObj = (PageRelocator*)relocator->object; 845 | return relocatorObj->cancelRelocation(); 846 | } 847 | 848 | bool pagerelocator_RestorePage(pagerelocator* relocator, virt_addr_t address) 849 | { 850 | if (relocator == nullptr) 851 | return false; 852 | 853 | if (relocator->object == nullptr) 854 | return false; 855 | 856 | auto relocatorObj = (PageRelocator*)relocator->object; 857 | return relocatorObj->restorePageFor(address); 858 | } 859 | 860 | void pagerelocator_Close(pagerelocator* relocator) 861 | { 862 | if (relocator == nullptr) 863 | return; 864 | 865 | auto relocatorObj = (PageRelocator*)relocator->object; 866 | delete relocatorObj; 867 | relocator->object = nullptr; 868 | 869 | pagerelocatorPrimitives::close(); 870 | } 871 | } 872 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VMAPlatform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #ifndef __cplusplus 14 | typedef uint64_t phys_addr_t; 15 | typedef uint64_t virt_addr_t; 16 | typedef uint64_t offset_t; 17 | typedef uint64_t ttentry_t; 18 | 19 | static const uint32_t kPlatformAddressSize = sizeof(virt_addr_t); 20 | static const uint32_t kPlatformAddressBits = kPlatformAddressSize * 8; 21 | static const offset_t kInvalidAddress = -1; 22 | static const offset_t kInvalidAddressOffset = -1; 23 | #endif 24 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VMAPlatform.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | using phys_addr_t = uint64_t; 15 | using virt_addr_t = uint64_t; 16 | using offset_t = uint64_t; 17 | using ttentry_t = uint64_t; 18 | 19 | static const uint32_t kPlatformAddressSize = sizeof(virt_addr_t); 20 | static const uint32_t kPlatformAddressBits = kPlatformAddressSize * 8; 21 | static const offset_t kInvalidAddress = -1; 22 | static const offset_t kInvalidAddressOffset = -1; 23 | 24 | // Input address (IA) using the 4K translation granule 25 | // D4.2 The VMSAv8-64 address translation system (Figure D4-3) 26 | using vm_addr_4k = union { 27 | struct { 28 | virt_addr_t outputAddress : 12; // IA[11:0] 29 | virt_addr_t tableIndexL3 : 9; // IA[20:12] 30 | virt_addr_t tableIndexL2 : 9; // IA[29:21] 31 | virt_addr_t tableIndexL1 : 9; // IA[38:30] 32 | virt_addr_t tableIndexL0 : 9; // IA[47:39] 33 | virt_addr_t ttbrRange : 16; 34 | } details; 35 | 36 | virt_addr_t value; 37 | }; 38 | 39 | // Input address (IA) using the 16K translation granule 40 | // D4.2 The VMSAv8-64 address translation system (Figure D4-4) 41 | using vm_addr_16k = union { 42 | struct { 43 | virt_addr_t outputAddress : 14; // IA[13:0] 44 | virt_addr_t tableIndexL3 : 11; // IA[24:14] 45 | virt_addr_t tableIndexL2 : 11; // IA[35:25] 46 | virt_addr_t tableIndexL1 : 11; // IA[46:36] 47 | virt_addr_t tableIndexL0 : 1; // IA[47] 48 | virt_addr_t ttbrRange : 16; 49 | } details; 50 | 51 | virt_addr_t value; 52 | }; 53 | 54 | // Input address (IA) using the 16K translation granule 55 | // D4.2 The VMSAv8-64 address translation system (Figure D4-5) 56 | using vm_addr_64k = union { 57 | struct { 58 | virt_addr_t outputAddress : 16; // IA[15:0] 59 | virt_addr_t tableIndexL3 : 13; // IA[28:16] 60 | virt_addr_t tableIndexL2 : 13; // IA[41:29] 61 | virt_addr_t tableIndexL1 : 6; // IA[47:42] 62 | virt_addr_t ttbrRange : 16; 63 | } details; 64 | 65 | virt_addr_t value; 66 | }; 67 | 68 | 69 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VMATypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #ifndef __cplusplus 12 | 13 | typedef enum { 14 | kExceptionLevel0 = 0, 15 | kExceptionLevel1 = 1, 16 | kExceptionLevel2 = 2, 17 | kExceptionLevel3 = 3, 18 | kExceptionLevelCount, 19 | kExceptionLevelUndefined = -1, 20 | } ExceptionLevel; 21 | 22 | typedef enum { 23 | kTTLevel0 = 0, 24 | kTTLevel1 = 1, 25 | kTTLevel2 = 2, 26 | kTTLevel3 = 3, 27 | kTTLevelCount, 28 | kTTLevelUndefined = -1, 29 | } TTLevel; 30 | 31 | typedef enum { 32 | kTTGranule4K = 4 * 1024, 33 | kTTGranule16K = 16 * 1024, 34 | kTTGranule64K = 64 * 1024, 35 | kTTGranuleCount = 3, 36 | kTTGranuleUndefined = -1, 37 | } TTGranule; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VMATypes.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | enum class ExceptionLevel { 12 | EL0 = 0, 13 | EL1 = 1, 14 | EL2 = 2, 15 | EL3 = 3, 16 | Count, 17 | Undefined = -1 18 | }; 19 | 20 | enum class TTLevel { 21 | Level0 = 0, 22 | Level1 = 1, 23 | Level2 = 2, 24 | Level3 = 3, 25 | Count, 26 | Undefined = -1 27 | }; 28 | 29 | TTLevel& operator++(TTLevel& level) 30 | { 31 | switch(level) 32 | { 33 | case TTLevel::Level0: return level = TTLevel::Level1; 34 | case TTLevel::Level1: return level = TTLevel::Level2; 35 | case TTLevel::Level2: return level = TTLevel::Level3; 36 | case TTLevel::Level3: return level = TTLevel::Level3; 37 | case TTLevel::Count: { assert(0); } 38 | case TTLevel::Undefined: { assert(0); } 39 | } 40 | }; 41 | 42 | TTLevel& operator++(TTLevel& level, int) 43 | { 44 | switch(level) 45 | { 46 | case TTLevel::Level0: return level = TTLevel::Level1; 47 | case TTLevel::Level1: return level = TTLevel::Level2; 48 | case TTLevel::Level2: return level = TTLevel::Level3; 49 | case TTLevel::Level3: return level = TTLevel::Level3; 50 | case TTLevel::Count: { assert(0); } 51 | case TTLevel::Undefined: { assert(0); } 52 | } 53 | }; 54 | 55 | TTLevel& operator--(TTLevel& level) 56 | { 57 | switch(level) 58 | { 59 | case TTLevel::Level0: return level = TTLevel::Level0; 60 | case TTLevel::Level1: return level = TTLevel::Level0; 61 | case TTLevel::Level2: return level = TTLevel::Level1; 62 | case TTLevel::Level3: return level = TTLevel::Level2; 63 | case TTLevel::Count: { assert(0); } 64 | case TTLevel::Undefined: { assert(0); } 65 | } 66 | }; 67 | 68 | TTLevel& operator--(TTLevel& level, int) 69 | { 70 | switch(level) 71 | { 72 | case TTLevel::Level0: return level = TTLevel::Level0; 73 | case TTLevel::Level1: return level = TTLevel::Level0; 74 | case TTLevel::Level2: return level = TTLevel::Level1; 75 | case TTLevel::Level3: return level = TTLevel::Level2; 76 | case TTLevel::Count: { assert(0); } 77 | case TTLevel::Undefined: { assert(0); } 78 | } 79 | }; 80 | 81 | enum class TTGranule { 82 | Granule4K = 4 * 1024, 83 | Granule16K = 16 * 1024, 84 | Granule64K = 64 * 1024, 85 | Count = 3, 86 | Undefined = -1 87 | }; 88 | -------------------------------------------------------------------------------- /MMUit/VMAKit/VirtualAddress.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | 14 | template struct VirtualAddressType {}; 15 | 16 | template <> struct VirtualAddressType 17 | { 18 | using Type = vm_addr_4k; 19 | }; 20 | 21 | template <> struct VirtualAddressType 22 | { 23 | using Type = vm_addr_16k; 24 | }; 25 | 26 | template <> struct VirtualAddressType 27 | { 28 | using Type = vm_addr_64k; 29 | }; 30 | 31 | class GenericVirtualAddress 32 | { 33 | virtual offset_t getOffsetForLevel(TTLevel level) = 0; 34 | virtual offset_t getOffsetForLevel(uint32_t level) = 0; 35 | }; 36 | 37 | template 38 | class VirtualAddress : public GenericVirtualAddress 39 | { 40 | public: 41 | 42 | using VirtualAddressType = typename VirtualAddressType::Type; 43 | 44 | VirtualAddress() = delete; 45 | 46 | VirtualAddress(VirtualAddressType address, uint32_t regionSizeOffset = 0) 47 | : m_virtAddress(address), m_regionSizeOffset(regionSizeOffset) 48 | { assert(regionSizeOffset < kPlatformAddressBits); } 49 | 50 | VirtualAddress(virt_addr_t address, uint32_t regionSizeOffset = 0) 51 | : m_virtAddress({.value = address}), m_regionSizeOffset(regionSizeOffset) 52 | { assert(regionSizeOffset < kPlatformAddressBits); } 53 | 54 | virt_addr_t rawValue() 55 | { 56 | return m_virtAddress.value; 57 | } 58 | 59 | VirtualAddressType virtualAddress() 60 | { 61 | return m_virtAddress; 62 | } 63 | 64 | uint32_t regionSizeOffset() 65 | { 66 | return m_regionSizeOffset; 67 | } 68 | 69 | offset_t getOffsetForLevel(TTLevel level) override 70 | { 71 | VirtualAddressType va({.value = m_virtAddress.value & ((virt_addr_t(1) << (kPlatformAddressBits - m_regionSizeOffset)) - 1)}); 72 | 73 | switch (level) 74 | { 75 | case TTLevel::Level0: return va.details.tableIndexL0 * kPlatformAddressSize; 76 | case TTLevel::Level1: return va.details.tableIndexL1 * kPlatformAddressSize; 77 | case TTLevel::Level2: return va.details.tableIndexL2 * kPlatformAddressSize; 78 | case TTLevel::Level3: return va.details.tableIndexL3 * kPlatformAddressSize; 79 | default: assert(0); 80 | } 81 | } 82 | 83 | offset_t getOffsetForLevel(uint32_t level) override 84 | { 85 | if (level < (uint32_t)TTLevel::Count) 86 | return getOffsetForLevel((TTLevel)level); 87 | else 88 | return kInvalidAddressOffset; 89 | } 90 | 91 | private: 92 | 93 | VirtualAddressType m_virtAddress; 94 | uint32_t m_regionSizeOffset; 95 | }; 96 | 97 | template <> 98 | offset_t VirtualAddress::getOffsetForLevel(TTLevel level) 99 | { 100 | assert(level != TTLevel::Level0); 101 | 102 | VirtualAddressType va({.value = m_virtAddress.value & ((virt_addr_t(1) << (kPlatformAddressBits - m_regionSizeOffset)) - 1) }); 103 | 104 | switch (level) 105 | { 106 | case TTLevel::Level1: return va.details.tableIndexL1 * kPlatformAddressSize; 107 | case TTLevel::Level2: return va.details.tableIndexL2 * kPlatformAddressSize; 108 | case TTLevel::Level3: return va.details.tableIndexL3 * kPlatformAddressSize; 109 | default: assert(0); 110 | } 111 | } 112 | 113 | using VA4K = VirtualAddress; 114 | using VA16K = VirtualAddress; 115 | using VA64K = VirtualAddress; 116 | 117 | -------------------------------------------------------------------------------- /MMUitTestC/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | #include "MMUit.h" 13 | 14 | // MARK: - MMU emulation 15 | 16 | // LEVEL 0 -> LEVEL 1 -> LEVEL 2.1 -> LEVEL 3.0 -> PAGE: 0xAAAAAAAA11111111 17 | // | | 18 | // | -> LEVEL 3.1 -> PAGE: 0xBBBBBBBB11111111 19 | // | 20 | // -> LEVEL 2.3 -> LEVEL 3.2 -> PAGE: 0xCCCCCCCC11111111 21 | // | 22 | // -> LEVEL 3.3 -> PAGE: 0xDDDDDDDD22222222 23 | 24 | // LEVEL 0 -> LEVEL 1 -> LEVEL 2.0 -> LEVEL 3.0 25 | 26 | // Use Granule4K (12 bit address offset) 27 | enum { E0, E1, E2, E3 }; 28 | enum { L0, L1, L2, L3 }; 29 | const uint32_t kAddressBitOffset = 12; 30 | const uint32_t kTableIndexShift = 4; 31 | 32 | // convert platform address to index and set valid and table bit if necessary 33 | #define MakeEntry(arrayIdx, tableOrPage) \ 34 | ((((arrayIdx << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize) | ((tableOrPage)? 0x2 : 0x0) | (0x1)) 35 | 36 | virt_addr_t MakeEntryIndex(uint64_t index, uint64_t level) 37 | { 38 | return index << (kAddressBitOffset + ((3 - level) * 9)); 39 | } 40 | 41 | virt_addr_t MakeVA(uint64_t e0Idx, uint64_t e1Idx, uint64_t e2Idx, uint64_t e3Idx, uint64_t offset) 42 | { 43 | return MakeEntryIndex(e0Idx, L0) | 44 | MakeEntryIndex(e1Idx, L1) | 45 | MakeEntryIndex(e2Idx, L2) | 46 | MakeEntryIndex(e3Idx, L3) | 47 | (offset * kPlatformAddressSize); // convert index to platform address 48 | } 49 | 50 | uintptr_t GetLevelIndex(uintptr_t address) 51 | { 52 | address /= kPlatformAddressSize; // convert platform address to index 53 | return (address >> kTableIndexShift) >> kAddressBitOffset; 54 | } 55 | 56 | uintptr_t GetEntryIndex(uintptr_t address) 57 | { 58 | address /= kPlatformAddressSize; // convert platform address to index 59 | return (address & 0xF); 60 | } 61 | 62 | ttentry_t TestTables[18][4] = { 63 | // Level 0 tables 64 | { MakeEntry(1, true), 0, 0, 0 }, // [0] LEVEL 0 65 | // Level 1 tables 66 | { 0, MakeEntry(3, true), 0, MakeEntry(5, true) }, // [1] LEVEL 1 67 | // Level 2 tables 68 | { 0, 0, 0, 0 }, // [2] LEVEL 2.0 69 | { 0, 0, MakeEntry(6, true), MakeEntry(7, true) }, // [3] LEVEL 2.1 70 | { 0, 0, 0, 0 }, // [4] LEVEL 2.2 71 | { MakeEntry(8, true), MakeEntry(9, true), 0, 0 }, // [5] LEVEL 2.3 72 | // Level 3 tables 73 | { 0, MakeEntry(10, true), 0, 0 }, // [6] LEVEL 3.0 74 | { 0, 0, 0, MakeEntry(11, true) }, // [7] LEVEL 3.1 75 | { MakeEntry(12, true), 0, 0, 0 }, // [8] LEVEL 3.2 76 | { 0, 0, MakeEntry(13, true), 0 }, // [9] LEVEL 3.3 77 | // Pages 78 | { 0xAAAAAAAA11111111, 0xAAAAAAAA22222222, 0xAAAAAAAA33333333, 0xAAAAAAAA44444444 }, // [10] PAGE 1 79 | { 0xBBBBBBBB11111111, 0xBBBBBBBB22222222, 0xBBBBBBBB33333333, 0xBBBBBBBB44444444 }, // [11] PAGE 2 80 | { 0xCCCCCCCC11111111, 0xCCCCCCCC22222222, 0xCCCCCCCC33333333, 0xCCCCCCCC44444444 }, // [12] PAGE 3 81 | { 0xDDDDDDDD11111111, 0xDDDDDDDD22222222, 0xDDDDDDDD33333333, 0xDDDDDDDD44444444 }, // [13] PAGE 4 82 | // Allocations 83 | { 0, 0, 0, 0 }, // [14] ALLOC 1 (LEVEL 2.1 COPY) 84 | { 0, 0, 0, 0 }, // [15] ALLOC 2 (LEVEL 3.0 COPY) 85 | { 0, 0, 0, 0 }, // [16] ALLOC 3 (PAGE 1 COPY) 86 | { 0, 0, 0, 0 }, // [17] ALLOC 4 87 | }; 88 | 89 | // MARK: - Primitives 90 | 91 | uintptr_t read_address(virt_addr_t address) 92 | { 93 | // address here is actually physical to simplify emulation 94 | return TestTables[GetLevelIndex(address)][GetEntryIndex(address)]; 95 | }; 96 | 97 | void write_address(virt_addr_t address, virt_addr_t data) 98 | { 99 | // address here is actually physical to simplify emulation 100 | TestTables[GetLevelIndex(address)][GetEntryIndex(address)] = data; 101 | } 102 | 103 | void copy_in_kernel(virt_addr_t dst, virt_addr_t src, uint32_t size) 104 | { 105 | // addresses here are actually physical to simplify emulation 106 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 0] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 0]; 107 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 1] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 1]; 108 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 2] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 2]; 109 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 3] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 3]; 110 | } 111 | 112 | virt_addr_t alloc_in_physical_memory(uint32_t size) 113 | { 114 | static uint32_t freePage = 14; 115 | 116 | uint32_t l1 = 0, l2 = 0, l3 = 0; 117 | uint32_t e1 = 0, e2 = 0, e3 = 0; 118 | 119 | // find free level 1 entry 120 | for (uint32_t e = 0; e < 4; e++) 121 | { 122 | if (TestTables[1][e] == 0) 123 | { 124 | l1 = 1; e1 = e; 125 | break; 126 | } 127 | } 128 | 129 | // find free level 2 entry 130 | for (uint32_t l = 2; l < 6; l++) 131 | { 132 | bool found = false; 133 | for (uint32_t e = 0; e < 4; e++) 134 | { 135 | if (TestTables[l][e] == 0) 136 | { 137 | l2 = l; e2 = e; 138 | found = true; 139 | break; 140 | } 141 | } 142 | if (found) 143 | break; 144 | } 145 | 146 | // find free level 3 entry 147 | for (uint32_t l = 6; l < 10; l++) 148 | { 149 | bool found = false; 150 | for (uint32_t e = 0; e < 4; e++) 151 | { 152 | if (TestTables[l][e] == 0) 153 | { 154 | l3 = l; e3 = e; 155 | found = true; 156 | break; 157 | } 158 | } 159 | if (found) 160 | break; 161 | } 162 | 163 | // update translation table 164 | TestTables[l1][e1] = MakeEntry(l2, true); 165 | TestTables[l2][e2] = MakeEntry(l3, true); 166 | TestTables[l3][e3] = MakeEntry(freePage, true); 167 | 168 | // create virtual address 169 | // return MakeVA(E0, e1, e2, e3, 0); 170 | 171 | // to simplify emulation return physical address here 172 | virt_addr_t address = ((freePage << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize; 173 | freePage++; 174 | 175 | return address; 176 | } 177 | 178 | bool dealloc_in_physical_memory(virt_addr_t address, uint32_t size) 179 | { 180 | return true; 181 | } 182 | 183 | virt_addr_t physical_to_virtual (phys_addr_t address) 184 | { 185 | // treat physical address as virtual to simplify emulation 186 | return address; 187 | } 188 | 189 | phys_addr_t virtual_to_physical (virt_addr_t address) 190 | { 191 | // treat virtual address as physical to simplify emulation 192 | return address; 193 | } 194 | 195 | // MARK: - Callbacks 196 | 197 | WalkOperation forwardwalk_callback(WalkPosition* position, TTEntryDetails* entry, uintptr_t user_data) 198 | { 199 | printf(" Level%d: %c%c%c [%.2lu][%.2lu]\n", position->level, 200 | (ttentry_IsValid(entry))? 'v' : '-', 201 | (ttentry_IsTableDescriptor(entry))? 't' : '-', 202 | (ttentry_IsPageDescriptor(entry))? 'p' : '-', 203 | GetLevelIndex(position->tableAddress), GetEntryIndex(position->entryOffset)); 204 | return kWalkOperation_Continue; 205 | } 206 | 207 | WalkOperation reversewalk_callback(WalkPosition* position, TTEntryDetails* entry, uintptr_t user_data) 208 | { 209 | printf(" Level%d: %c%c%c [%.2lu][%.2lu]\n", position->level, 210 | (ttentry_IsValid(entry))? 'v' : '-', 211 | (ttentry_IsTableDescriptor(entry))? 't' : '-', 212 | (ttentry_IsPageDescriptor(entry))? 'p' : '-', 213 | GetLevelIndex(position->tableAddress), GetEntryIndex(position->entryOffset)); 214 | return kWalkOperation_Continue; 215 | } 216 | 217 | ttentry_t relocator_callback(TTLevel level, TTEntryDetails* oldEntry, TTEntryDetails* newEntry, uintptr_t user_data) 218 | { 219 | printf(" MOVE: 0x%.16llX -> 0x%.16llX | [%.2lu][] -> [%.2lu][]\n", 220 | ttentry_GetOutputAddress(oldEntry), 221 | ttentry_GetOutputAddress(newEntry), 222 | GetLevelIndex(ttentry_GetOutputAddress(oldEntry)), GetLevelIndex(ttentry_GetOutputAddress(newEntry))); 223 | // patch data in new entry 224 | if (ttentry_IsPageDescriptor(newEntry)) { 225 | ttentry_SetXN(newEntry, false); 226 | ttentry_SetPXN(newEntry, false); 227 | write_address(physical_to_virtual(ttentry_GetOutputAddress(newEntry)), 0xDEADBEEFDEADBEEF); 228 | } 229 | return newEntry->descriptor; 230 | } 231 | 232 | // MARK: - main 233 | 234 | int main(int argc, const char * argv[]) 235 | { 236 | uintptr_t value; 237 | virt_addr_t vaddr; 238 | phys_addr_t paddr; 239 | 240 | tcr_el1_t tcr_el1 = 0x2A51C251C; 241 | 242 | printf("\n*** TEST MMU Config\n"); 243 | 244 | MMUConfig mmuConfig; 245 | mmuconfigparser mmuConfigParser; 246 | 247 | mmuconfig_Init(&mmuConfigParser); 248 | mmuconfig_SetTCR_EL1(&mmuConfigParser, tcr_el1); 249 | 250 | mmuConfig = mmuconfig_GetConfigFor(&mmuConfigParser, kExceptionLevel1); 251 | 252 | printf("TCR: 0x%.16llX\n", tcr_el1); 253 | printf(" Granule: %u\n", mmuConfig.granule); 254 | printf(" InitialLevel: %u\n", mmuConfig.initial_level); 255 | printf(" RegionSizeOffset: %u\n", mmuConfig.region_size_offset); 256 | assert(mmuConfig.granule == kTTGranule4K && mmuConfig.initial_level == kTTLevel1 && mmuConfig.region_size_offset == 28); 257 | 258 | virt_addr_t ttbr = ((mmuConfig.initial_level << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize; 259 | 260 | ttwalker walker = {0}; 261 | walker.mmu_config = mmuConfig; 262 | walker.table_base = ttbr; 263 | walker.read_address = read_address; 264 | walker.physical_to_virtual = physical_to_virtual; 265 | 266 | pagerelocator relocator = {0}; 267 | relocator.mmu_config = mmuConfig; 268 | relocator.table_base = ttbr; 269 | relocator.read_address = read_address; 270 | relocator.write_address = write_address; 271 | relocator.copy_in_kernel = copy_in_kernel; 272 | relocator.alloc_in_physical_memory = alloc_in_physical_memory; 273 | relocator.dealloc_in_physical_memory = dealloc_in_physical_memory; 274 | relocator.physical_to_virtual = physical_to_virtual; 275 | relocator.virtual_to_physical = virtual_to_physical; 276 | 277 | printf("\n*** TEST findPhysicalAddress()\n"); 278 | 279 | vaddr = MakeVA(E0, E1, E2, E1, 0); 280 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 281 | value = read_address(physical_to_virtual(paddr)); 282 | printf("[0] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 283 | assert(value == 0xAAAAAAAA11111111); 284 | 285 | vaddr = MakeVA(E0, E1, E3, E3, 1); 286 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 287 | value = read_address(physical_to_virtual(paddr)); 288 | printf("[1] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 289 | assert(value == 0xBBBBBBBB22222222); 290 | 291 | vaddr = MakeVA(E0, E3, E0, E0, 2); 292 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 293 | value = read_address(physical_to_virtual(paddr)); 294 | printf("[2] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 295 | assert(value == 0xCCCCCCCC33333333); 296 | 297 | vaddr = MakeVA(E0, E3, E1, E2, 3); 298 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 299 | value = read_address(physical_to_virtual(paddr)); 300 | printf("[3] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 301 | assert(value == 0xDDDDDDDD44444444); 302 | 303 | printf("\n*** TEST walkTo()\n"); 304 | 305 | WalkResult walkResult; 306 | vaddr = MakeVA(E0, E1, E3, E3, 0); 307 | printf("Address: 0x%.16llX\n", vaddr); 308 | walkResult = ttwalker_Walk(&walker, vaddr, forwardwalk_callback); 309 | assert(walkResult.type == kWalkResultType_Complete); 310 | 311 | paddr = walkResult.outputAddress; 312 | printf(" page: [%.2lu][%.2lu] = 0x%.16lX\n", 313 | GetLevelIndex(paddr), 314 | GetEntryIndex(paddr), 315 | read_address(physical_to_virtual(paddr))); 316 | 317 | printf("\n*** TEST reverseWalkFrom()\n"); 318 | 319 | bool reverseResult; 320 | vaddr = MakeVA(E0, E1, E3, E3, 0); 321 | printf("Address: 0x%.16llX\n", vaddr); 322 | 323 | reverseResult = ttwalker_ReverseWalk(&walker, vaddr, reversewalk_callback); 324 | assert(reverseResult == true); 325 | 326 | printf("\n*** TEST relocatePageFor()\n"); 327 | 328 | vaddr = MakeVA(E0, E1, E3, E3, 0); 329 | 330 | // check before state 331 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 332 | value = read_address(physical_to_virtual(paddr)); 333 | printf("ORIGINAL: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 334 | assert(value == 0xBBBBBBBB11111111); 335 | 336 | bool relocateResult; 337 | pagerelocator_Init(&relocator); 338 | relocateResult = pagerelocator_RelocatePage(&relocator, vaddr, relocator_callback); 339 | assert(relocateResult == true); 340 | 341 | // check after state 342 | vaddr = MakeVA(E0, E1, E3, E3, 0); 343 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 344 | value = read_address(physical_to_virtual(paddr)); 345 | printf("RELOCATE: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 346 | assert(value == 0xDEADBEEFDEADBEEF); 347 | 348 | printf("\n*** TEST preparePageRelocationFor()\n"); 349 | 350 | bool cancel = false; 351 | vaddr = MakeVA(E0, E1, E2, E1, 0); 352 | 353 | // check before state 354 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 355 | value = read_address(physical_to_virtual(paddr)); 356 | printf("ORIGINAL: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 357 | assert(value == 0xAAAAAAAA11111111); 358 | 359 | virt_addr_t newPageVA; 360 | newPageVA = pagerelocator_PreparePageRelocation(&relocator, vaddr, relocator_callback); 361 | assert(newPageVA != kInvalidAddress); 362 | 363 | // patch data in new entry 364 | write_address(newPageVA, 0xDEADBEEFDEADBEEF); 365 | 366 | printf("\n*** TEST cancelRelocation()\n"); 367 | 368 | if (cancel) { 369 | // cancel relocation 370 | pagerelocator_CancelRelocation(&relocator); 371 | } else { 372 | // complete relocation 373 | pagerelocator_CompleteRelocation(&relocator); 374 | } 375 | 376 | // check after state 377 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 378 | value = read_address(physical_to_virtual(paddr)); 379 | printf("CANCELED: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 380 | assert(value == 0xAAAAAAAA11111111); 381 | 382 | printf("\n*** TEST restorePageFor()\n"); 383 | 384 | vaddr = MakeVA(E0, E1, E3, E3, 0); 385 | 386 | // restore original page 387 | bool restoreResult; 388 | restoreResult = pagerelocator_RestorePage(&relocator, vaddr); 389 | assert(restoreResult == true); 390 | 391 | // check after restore 392 | paddr = ttwalker_FindPhysicalAddress(&walker, vaddr); 393 | value = read_address(physical_to_virtual(paddr)); 394 | printf(" RESTORE: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 395 | assert(value == 0xBBBBBBBB11111111); 396 | 397 | pagerelocator_Close(&relocator); 398 | mmuconfig_Close(&mmuConfigParser); 399 | 400 | printf("\n*** TEST COMPLETE\n"); 401 | return 0; 402 | } 403 | -------------------------------------------------------------------------------- /MMUitTestCPP/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #include "MMUit.hpp" 10 | 11 | #include 12 | 13 | // MARK: - MMU emulation 14 | 15 | // LEVEL 0 -> LEVEL 1 -> LEVEL 2.1 -> LEVEL 3.0 -> PAGE: 0xAAAAAAAA11111111 16 | // | | 17 | // | -> LEVEL 3.1 -> PAGE: 0xBBBBBBBB11111111 18 | // | 19 | // -> LEVEL 2.3 -> LEVEL 3.2 -> PAGE: 0xCCCCCCCC11111111 20 | // | 21 | // -> LEVEL 3.3 -> PAGE: 0xDDDDDDDD22222222 22 | 23 | // LEVEL 0 -> LEVEL 1 -> LEVEL 2.0 -> LEVEL 3.0 24 | 25 | // Use Granule4K (12 bit address offset) 26 | enum { E0, E1, E2, E3 }; 27 | enum { L0, L1, L2, L3 }; 28 | const uint32_t kAddressBitOffset = 12; 29 | const uint32_t kTableIndexShift = 4; 30 | 31 | constexpr ttentry_t MakeEntry(uint32_t arrayIdx, bool tableOrPage) 32 | { 33 | // convert platform address to index and set valid and table bit if necessary 34 | return (((arrayIdx << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize) | ((tableOrPage)? 0x2 : 0x0) | (0x1); 35 | } 36 | 37 | constexpr virt_addr_t MakeEntryIndex(uint64_t index, uint64_t level) 38 | { 39 | return index << (kAddressBitOffset + ((3 - level) * 9)); 40 | } 41 | 42 | constexpr virt_addr_t MakeVA(uint64_t e0Idx, uint64_t e1Idx, uint64_t e2Idx, uint64_t e3Idx, uint64_t offset) 43 | { 44 | return MakeEntryIndex(e0Idx, L0) | 45 | MakeEntryIndex(e1Idx, L1) | 46 | MakeEntryIndex(e2Idx, L2) | 47 | MakeEntryIndex(e3Idx, L3) | 48 | (offset * kPlatformAddressSize); // convert index to platform address 49 | } 50 | 51 | uintptr_t GetLevelIndex(uintptr_t address) 52 | { 53 | address /= kPlatformAddressSize; // convert platform address to index 54 | return (address >> kTableIndexShift) >> kAddressBitOffset; 55 | } 56 | 57 | uintptr_t GetEntryIndex(uintptr_t address) 58 | { 59 | address /= kPlatformAddressSize; // convert platform address to index 60 | return (address & 0xF); 61 | } 62 | 63 | ttentry_t TestTables[19][4] = { 64 | // Level 0 tables 65 | { MakeEntry(1, true), 0, 0, 0 }, // [0] LEVEL 0 66 | // Level 1 tables 67 | { 0, MakeEntry(3, true), 0, MakeEntry(5, true) }, // [1] LEVEL 1 68 | // Level 2 tables 69 | { 0, 0, 0, 0 }, // [2] LEVEL 2.0 70 | { 0, 0, MakeEntry(6, true), MakeEntry(7, true) }, // [3] LEVEL 2.1 71 | { 0, 0, 0, 0 }, // [4] LEVEL 2.2 72 | { MakeEntry(8, true), MakeEntry(9, true), 0, 0 }, // [5] LEVEL 2.3 73 | // Level 3 tables 74 | { 0, MakeEntry(10, true), 0, 0 }, // [6] LEVEL 3.0 75 | { 0, 0, 0, MakeEntry(11, true) }, // [7] LEVEL 3.1 76 | { MakeEntry(12, true), 0, 0, 0 }, // [8] LEVEL 3.2 77 | { 0, 0, MakeEntry(13, true), 0 }, // [9] LEVEL 3.3 78 | // Pages 79 | { 0xAAAAAAAA11111111, 0xAAAAAAAA22222222, 0xAAAAAAAA33333333, 0xAAAAAAAA44444444 }, // [10] PAGE 1 80 | { 0xBBBBBBBB11111111, 0xBBBBBBBB22222222, 0xBBBBBBBB33333333, 0xBBBBBBBB44444444 }, // [11] PAGE 2 81 | { 0xCCCCCCCC11111111, 0xCCCCCCCC22222222, 0xCCCCCCCC33333333, 0xCCCCCCCC44444444 }, // [12] PAGE 3 82 | { 0xDDDDDDDD11111111, 0xDDDDDDDD22222222, 0xDDDDDDDD33333333, 0xDDDDDDDD44444444 }, // [13] PAGE 4 83 | // Allocations 84 | { 0, 0, 0, 0 }, // [14] ALLOC 1 (LEVEL 2.1 COPY) 85 | { 0, 0, 0, 0 }, // [15] ALLOC 2 (LEVEL 3.0 COPY) 86 | { 0, 0, 0, 0 }, // [16] ALLOC 3 (PAGE 1 COPY) 87 | { 0, 0, 0, 0 }, // [17] ALLOC 4 88 | { 0, 0, 0, 0 }, // [18] ALLOC 5 89 | }; 90 | 91 | // MARK: - MyPrimitives class 92 | 93 | class MyPrimitives : public Primitives 94 | { 95 | public: 96 | uintptr_t readAddress(virt_addr_t address) 97 | { 98 | // address here is actually physical to simplify emulation 99 | return TestTables[GetLevelIndex(address)][GetEntryIndex(address)]; 100 | }; 101 | 102 | void writeAddress(virt_addr_t address, virt_addr_t data) 103 | { 104 | // address here is actually physical to simplify emulation 105 | TestTables[GetLevelIndex(address)][GetEntryIndex(address)] = data; 106 | } 107 | 108 | void copyInKernel(virt_addr_t dst, virt_addr_t src, uint32_t size) 109 | { 110 | // addresses here are actually physical to simplify emulation 111 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 0] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 0]; 112 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 1] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 1]; 113 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 2] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 2]; 114 | TestTables[GetLevelIndex(dst)][GetEntryIndex(dst) + 3] = TestTables[GetLevelIndex(src)][GetEntryIndex(src) + 3]; 115 | } 116 | 117 | virt_addr_t allocInPhysicalMemory(uint32_t size) 118 | { 119 | static uint32_t freePage = 14; 120 | 121 | uint32_t l1 = 0, l2 = 0, l3 = 0; 122 | uint32_t e1 = 0, e2 = 0, e3 = 0; 123 | 124 | // find free level 1 entry 125 | for (uint32_t e = 0; e < 4; e++) 126 | { 127 | if (TestTables[1][e] == 0) 128 | { 129 | l1 = 1; e1 = e; 130 | break; 131 | } 132 | } 133 | 134 | // find free level 2 entry 135 | for (uint32_t l = 2; l < 6; l++) 136 | { 137 | bool found = false; 138 | for (uint32_t e = 0; e < 4; e++) 139 | { 140 | if (TestTables[l][e] == 0) 141 | { 142 | l2 = l; e2 = e; 143 | found = true; 144 | break; 145 | } 146 | } 147 | if (found) 148 | break; 149 | } 150 | 151 | // find free level 3 entry 152 | for (uint32_t l = 6; l < 10; l++) 153 | { 154 | bool found = false; 155 | for (uint32_t e = 0; e < 4; e++) 156 | { 157 | if (TestTables[l][e] == 0) 158 | { 159 | l3 = l; e3 = e; 160 | found = true; 161 | break; 162 | } 163 | } 164 | if (found) 165 | break; 166 | } 167 | 168 | // update translation table 169 | TestTables[l1][e1] = MakeEntry(l2, true); 170 | TestTables[l2][e2] = MakeEntry(l3, true); 171 | TestTables[l3][e3] = MakeEntry(freePage, true); 172 | 173 | // create virtual address 174 | // return MakeVA(E0, e1, e2, e3, 0); 175 | 176 | // to simplify emulation return physical address here 177 | virt_addr_t address = ((freePage << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize; 178 | freePage++; 179 | 180 | return address; 181 | } 182 | 183 | bool deallocInPhysicalMemory(virt_addr_t address, uint32_t size) 184 | { 185 | return true; 186 | } 187 | 188 | virt_addr_t physicalToVirtual (phys_addr_t address) 189 | { 190 | // treat physical address as virtual to simplify emulation 191 | return address; 192 | } 193 | 194 | phys_addr_t virtualToPhysical (virt_addr_t address) 195 | { 196 | // treat virtual address as physical to simplify emulation 197 | return address; 198 | } 199 | 200 | }; 201 | 202 | // MARK: - main 203 | 204 | int main(int argc, const char * argv[]) 205 | { 206 | uintptr_t value; 207 | virt_addr_t vaddr; 208 | phys_addr_t paddr; 209 | 210 | TCR_EL1 tcr_el1(0x2A51C251C); 211 | 212 | printf("\n*** TEST MMU Config\n"); 213 | 214 | MMUConfig mmuConfig; 215 | MMUConfigParser mmuConfigParser; 216 | mmuConfigParser.setTCR_EL1(tcr_el1); 217 | 218 | mmuConfig = mmuConfigParser.getConfigFor(ExceptionLevel::EL1); 219 | printf("TCR: 0x%.16llX\n", tcr_el1.getValue()); 220 | printf(" Granule: %u\n", uint32_t(mmuConfig.granule)); 221 | printf(" InitialLevel: %u\n", uint32_t(mmuConfig.initialLevel)); 222 | printf(" RegionSizeOffset: %u\n", uint32_t(mmuConfig.regionSizeOffset)); 223 | assert(mmuConfig.granule == TTGranule::Granule4K && mmuConfig.initialLevel == TTLevel::Level1 && mmuConfig.regionSizeOffset == 28); 224 | 225 | virt_addr_t ttbr = ((uint32_t(mmuConfig.initialLevel) << kTableIndexShift) << kAddressBitOffset) * kPlatformAddressSize; 226 | TTWalker walker(mmuConfig, ttbr); 227 | PageRelocator relocator(mmuConfig, ttbr); 228 | 229 | printf("\n*** TEST findPhysicalAddress()\n"); 230 | 231 | vaddr = MakeVA(E0, E1, E2, E1, 0); 232 | paddr = walker.findPhysicalAddress(vaddr); 233 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 234 | printf("[0] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 235 | assert(value == 0xAAAAAAAA11111111); 236 | 237 | vaddr = MakeVA(E0, E1, E3, E3, 1); 238 | paddr = walker.findPhysicalAddress(vaddr); 239 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 240 | printf("[1] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 241 | assert(value == 0xBBBBBBBB22222222); 242 | 243 | vaddr = MakeVA(E0, E3, E0, E0, 2); 244 | paddr = walker.findPhysicalAddress(vaddr); 245 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 246 | printf("[2] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 247 | assert(value == 0xCCCCCCCC33333333); 248 | 249 | vaddr = MakeVA(E0, E3, E1, E2, 3); 250 | paddr = walker.findPhysicalAddress(vaddr); 251 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 252 | printf("[3] 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 253 | assert(value == 0xDDDDDDDD44444444); 254 | 255 | TTGenericWalker* genericWalker = &walker; 256 | 257 | printf("\n*** TEST walkTo()\n"); 258 | 259 | WalkResult walkResult; 260 | vaddr = MakeVA(E0, E1, E3, E3, 0); 261 | printf("Address: 0x%.16llX\n", vaddr); 262 | walkResult = genericWalker->walkTo(vaddr, [] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation { 263 | printf(" Level%d: %c%c%c [%.2lu][%.2lu]\n", position->level, 264 | (entry->isValid())? 'v' : '-', 265 | (entry->isTableDescriptor())? 't' : '-', 266 | (entry->isPageDescriptor())? 'p' : '-', 267 | GetLevelIndex(position->tableAddress), GetEntryIndex(position->entryOffset)); 268 | return WalkOperation::Continue; 269 | }); 270 | assert(walkResult.getType() == WalkResultType::Complete); 271 | 272 | paddr = walkResult.getOutputAddress(); 273 | printf(" page: [%.2lu][%.2lu] = 0x%.16lX\n", 274 | GetLevelIndex(paddr), 275 | GetEntryIndex(paddr), 276 | walker.readAddress(walker.physicalToVirtual(paddr))); 277 | 278 | printf("\n*** TEST reverseWalkFrom()\n"); 279 | 280 | bool reverseResult; 281 | vaddr = MakeVA(E0, E1, E3, E3, 0); 282 | printf("Address: 0x%.16llX\n", vaddr); 283 | 284 | // With defaul callback: genericWalker->reverseWalkFrom(vaddr, TTGenericWalker::DefaultCallback); 285 | reverseResult = genericWalker->reverseWalkFrom(vaddr, [] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation { 286 | printf(" Level%d: %c%c%c [%.2lu][%.2lu]\n", position->level, 287 | (entry->isValid())? 'v' : '-', 288 | (entry->isTableDescriptor())? 't' : '-', 289 | (entry->isPageDescriptor())? 'p' : '-', 290 | GetLevelIndex(position->tableAddress), GetEntryIndex(position->entryOffset)); 291 | return WalkOperation::Continue; 292 | }); 293 | assert(reverseResult == true); 294 | 295 | printf("\n*** TEST relocatePageFor()\n"); 296 | 297 | vaddr = MakeVA(E0, E1, E3, E3, 0); 298 | 299 | // check before state 300 | paddr = walker.findPhysicalAddress(vaddr); 301 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 302 | printf("ORIGINAL: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 303 | assert(value == 0xBBBBBBBB11111111); 304 | 305 | // With default callback: relocator.relocatePageFor(vaddr, PageRelocator::DefaultCallback); 306 | bool relocateResult; 307 | relocateResult = relocator.relocatePageFor(vaddr, [&relocator] (TTLevel level, TTGenericEntry* oldEntry, TTGenericEntry* newEntry) -> ttentry_t { 308 | printf(" MOVE: 0x%.16llX -> 0x%.16llX | [%.2lu][] -> [%.2lu][]\n", 309 | oldEntry->getOutputAddress(), 310 | newEntry->getOutputAddress(), 311 | GetLevelIndex(oldEntry->getOutputAddress()), GetLevelIndex(newEntry->getOutputAddress())); 312 | // patch data in new entry 313 | if (newEntry->isPageDescriptor()) { 314 | dynamic_cast(newEntry)->setXN(false); 315 | dynamic_cast(newEntry)->setPXN(false); 316 | relocator.writeAddress(relocator.physicalToVirtual(newEntry->getOutputAddress()), 0xDEADBEEFDEADBEEF); 317 | } 318 | return newEntry->getDescriptor(); 319 | }); 320 | assert(relocateResult == true); 321 | 322 | // check after state 323 | paddr = walker.findPhysicalAddress(vaddr); 324 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 325 | printf("RELOCATE: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 326 | assert(value == 0xDEADBEEFDEADBEEF); 327 | 328 | printf("\n*** TEST preparePageRelocationFor()\n"); 329 | 330 | bool cancel = false; 331 | vaddr = MakeVA(E0, E1, E2, E1, 0); 332 | 333 | // check before state 334 | paddr = walker.findPhysicalAddress(vaddr); 335 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 336 | printf("ORIGINAL: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 337 | assert(value == 0xAAAAAAAA11111111); 338 | 339 | virt_addr_t newPageVA; 340 | newPageVA = relocator.preparePageRelocationFor(vaddr, [&relocator] (TTLevel level, TTGenericEntry* oldEntry, TTGenericEntry* newEntry) -> ttentry_t { 341 | printf(" MOVE: 0x%.16llX -> 0x%.16llX | [%.2lu][] -> [%.2lu][]\n", 342 | oldEntry->getOutputAddress(), 343 | newEntry->getOutputAddress(), 344 | GetLevelIndex(oldEntry->getOutputAddress()), GetLevelIndex(newEntry->getOutputAddress())); 345 | return newEntry->getDescriptor(); 346 | }); 347 | assert(newPageVA != kInvalidAddress); 348 | 349 | // patch data in new entry 350 | relocator.writeAddress(newPageVA, 0xDEADBEEFDEADBEEF); 351 | 352 | printf("\n*** TEST cancelRelocation()\n"); 353 | 354 | if (cancel) { 355 | // cancel relocation 356 | relocateResult = relocator.cancelRelocation(); 357 | } else { 358 | // complete relocation 359 | relocateResult = relocator.completeRelocation(); 360 | } 361 | 362 | // check after state 363 | paddr = walker.findPhysicalAddress(vaddr); 364 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 365 | printf("CANCELED: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 366 | assert(value == 0xAAAAAAAA11111111); 367 | 368 | printf("\n*** TEST restorePageFor()\n"); 369 | 370 | vaddr = MakeVA(E0, E1, E3, E3, 0); 371 | 372 | // restore original page 373 | bool restoreResult; 374 | restoreResult = relocator.restorePageFor(vaddr); 375 | assert(restoreResult == true); 376 | 377 | // check after restore 378 | paddr = walker.findPhysicalAddress(vaddr); 379 | value = walker.readAddress(walker.physicalToVirtual(paddr)); 380 | printf(" RESTORE: 0x%.16llX -> 0x%.16llX : 0x%.16lX\n", vaddr, paddr, value); 381 | assert(value == 0xBBBBBBBB11111111); 382 | 383 | printf("\n*** TEST COMPLETE\n"); 384 | return 0; 385 | } 386 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | **MMUit** is a lightweight toolkit to explore and modify address translation for ARM64. 3 | 4 | - C/C++ interface 5 | - detailed information on **VA**, **TTE**, **TCR** etc 6 | - translation table walk (forward and reverse) 7 | - page relocation (Yalu jailbreak style) 8 | 9 | **NOTE:** it was mostly tested on iOS, not all translation modes are implemented. 10 | 11 | ## Usage 12 | 13 | #### Exploring MMU 14 | 15 | To get detailed information on particular items you can just feed objects with raw value and observe output in debugger. 16 | 17 | ![](./Resources/usage_details.png) 18 | 19 | Alternatively you can access particular fields and print them to console: 20 | 21 | ```cpp 22 | TTLevel3Entry_16K entry(0x0000000000580003); 23 | printf("PTE (L3, 16K): %.16llX\n", entry.getDescriptor()); 24 | printf(" valid: %s\n", entry.isValid()? "YES" : "NO"); 25 | printf(" table: %s\n", entry.isTableDescriptor()? "YES" : "NO"); 26 | printf(" page: %s\n", entry.isPageDescriptor()? "YES" : "NO"); 27 | printf(" PXN: %s\n", entry.getPXN()? "YES" : "NO"); 28 | printf(" XN: %s\n", entry.getXN()? "YES" : "NO"); 29 | printf(" address: 0x%llX\n", entry.getOutputAddress()); 30 | ``` 31 | Output: 32 | 33 | ``` 34 | PTE (L3, 16K): 0000000000580003 35 | valid: YES 36 | table: NO 37 | page: YES 38 | PXN: NO 39 | XN: NO 40 | address: 0x580000 41 | ``` 42 | 43 | It is also possible to debug translation by manually walking through the dumped tables: 44 | 45 | ```cpp 46 | TCR_EL1 tcr_el1(0x1651BA51B); 47 | virt_addr_t TTBR0_EL1 = 0x100024000; // PA of the translation table dump 48 | virt_addr_t va = 0x100005800; // target VA 49 | 50 | MMUConfig mmuConfig; 51 | MMUConfigParser mmuConfigParser; 52 | mmuConfigParser.setTCR_EL1(tcr_el1); 53 | mmuConfig = mmuConfigParser.getConfigFor(ExceptionLevel::EL0); 54 | 55 | virt_addr_t pageMask = uint32_t(mmuConfig.granule) - 1; 56 | VA16K addr(va, mmuConfig.regionSizeOffset); 57 | 58 | virt_addr_t tte_l1_pa = TTBR0_EL1 + addr.getOffsetForLevel(1); 59 | // tte_l1_pa<0x100024000> : 0x100028003 (L1 TTE from the dump) 60 | TTLevel1Entry_16K tte_l1(0x100028003); 61 | 62 | virt_addr_t tte_l2_pa = tte_l1.getOutputAddress() + addr.getOffsetForLevel(2); 63 | // tte_l2_pa<0x100028400>: 0x10002C003 (L2 TTE from the dump) 64 | TTLevel2Entry_16K tte_l2(0x10002C003); 65 | 66 | uint64_t tte_l3_pa = tte_l2.getOutputAddress() + addr.getOffsetForLevel(3); 67 | // tte_l3_pa<0x10002C008>: 0x48000100004E03 (L3 TTE from the dump) 68 | TTLevel3Entry_16K tte_l3(0x48000100004E03); 69 | 70 | virt_addr_t pa = tte_l3.getOutputAddress() | (addr.rawValue() & pageMask); 71 | ``` 72 | 73 | #### Primitives 74 | 75 | To work on real system it is required to implement your own `Primitives` class (or callback functions for C interface). It is used by **MMUit** to read/write kernel memory, PA→VA conversion, allocations etc. 76 | 77 | For example, FriedApple jailbreak used **copyin**/**copyout** primitives to deal with kernel memory and **gPhysBase**/**gVirtBase** to convert physical address: 78 | 79 | ```c 80 | static uintptr_t read_address(virt_addr_t address) { 81 | uint64_t val = 0; 82 | copyin(&val, addr, 8); 83 | return val; 84 | }; 85 | 86 | static void write_address(virt_addr_t address, virt_addr_t data) { 87 | copyout(addr, &data, 8); 88 | } 89 | 90 | static virt_addr_t physical_to_virtual (phys_addr_t address) { 91 | return address - gPhysBase + gVirtBase; 92 | } 93 | 94 | static virt_addr_t virtual_to_physical (phys_addr_t address) { 95 | ttwalker walker = {0}; 96 | walker.granule = kTTGranule16K; 97 | walker.start_level = kTTLevel1; 98 | walker.table_base = physalloc(0x4000); 99 | walker.read_address = read_address; 100 | walker.physical_to_virtual = physical_to_virtual; 101 | return ttwalker_FindPhysicalAddress(&walker, address); 102 | } 103 | ``` 104 | #### MMUConfig 105 | 106 | Contains information about current MMU configuration and should be passed to **Walker** or **PageRelocator**. 107 | 108 | ```cpp 109 | TCR_EL1 tcr_el1(0x2A51C251C); 110 | 111 | MMUConfig mmuConfig; 112 | MMUConfigParser mmuConfigParser; 113 | mmuConfigParser.setTCR_EL1(tcr_el1); 114 | 115 | mmuConfig = mmuConfigParser.getConfigFor(ExceptionLevel::EL1); 116 | TTWalker walker(mmuConfig, TTBR_VA); 117 | PageRelocator relocator(mmuConfig, TTBR_VA); 118 | ``` 119 | 120 | #### Walker 121 | 122 | Allows to find **PA** (physical address) for particular **VA** (virtual address) moving through translation tables and executing callback function for every level to help debugging translation path. **Forward walk** is calling callbacks from **L0** to **L3** while **reverse walk** is calling callbacks from **L3** to **L0**. 123 | 124 | ```cpp 125 | WalkResult walkResult = genericWalker->walkTo(TARGET_VA, 126 | [] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation { 127 | printf(" Level%d: %c%c%c [%.2lu][%.2lu]\n", position->level, 128 | (entry->isValid())? 'v' : '-', 129 | (entry->isTableDescriptor())? 't' : '-', 130 | (entry->isPageDescriptor())? 'p' : '-', 131 | GetLevelIndex(position->tableAddress), GetEntryIndex(position->entryOffset)); 132 | return WalkOperation::Continue; 133 | }); 134 | ``` 135 | 136 | #### PageRelocator 137 | 138 | Provides functions to duplicate existing pages by relocating them using alternative translation path. Relocator also supports callbacks which can be used to modify TTE flags or data for duplicated page on a fly during relocation. 139 | Another option is to stage relocation process using `preparePageRelocationFor` (`pagerelocator_PreparePageRelocation` in C) function which returns VA of duplicated page to be modified separately. Then it is possible to call `cancelRelocation` to restore original translation path or `completeRelocation` to apply final modification to page TTE. 140 | 141 | ```cpp 142 | virt_addr_t newPageVA = relocator.preparePageRelocationFor(TARGET_VA, 143 | [&relocator] (TTLevel level, TTGenericEntry* oldEntry, TTGenericEntry* newEntry) -> ttentry_t { 144 | printf(" MOVE: 0x%.16llX -> 0x%.16llX | [%.2lu][] -> [%.2lu][]\n", 145 | oldEntry->getOutputAddress(), 146 | newEntry->getOutputAddress(), 147 | GetLevelIndex(oldEntry->getOutputAddress()), GetLevelIndex(newEntry->getOutputAddress())); 148 | return newEntry->getDescriptor(); 149 | }); 150 | 151 | uint64_t data = 0xDEADBEEFDEADBEEF; 152 | copyout(newPageVA, &data, 8); 153 | 154 | relocator.completeRelocation(); 155 | ``` 156 | 157 | Page relocation is a known trick to patch kernel (Yalu jailbreak) which is described in details during **Fried Apples: Jailbreak DIY** keynote at BlackHat Asia 2017. In short it looks like that: 158 | 159 | ![](./Resources/usage_fake_tt.png) 160 | 161 | ## Examples 162 | 163 | ### C++ 164 | 165 | `MMUIitTestCPP/main.cpp` contains demonstration and tests of main MMUit functionality using manually crafted translation table: 166 | 167 | 1. finds page PA from VA and reads data at this address 168 | 2. uses forward walk to translate to VA logging TTE along the way 169 | 3. uses reverse walk to translete to VA logging TTE along the way 170 | 4. duplicates page for VA logging TTE and changing its XN/PXN flags and data on a fly during relocation 171 | 5. prepares duplicate page for VA logging TTE and returns address of that page 172 | 6. patches data for duplicated page 173 | 7. completes relocation (or cancels if `cancel = true`) 174 | 8. restores original translation path for (4) 175 | 176 | ### C 177 | 178 | `MMUIitTestC/main.c` is functionally identical to C++ example above. 179 | -------------------------------------------------------------------------------- /Resources/usage_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexhude/MMUit/94ad551825df05cb22a6567396aa41aa50e1c55a/Resources/usage_details.png -------------------------------------------------------------------------------- /Resources/usage_fake_tt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexhude/MMUit/94ad551825df05cb22a6567396aa41aa50e1c55a/Resources/usage_fake_tt.png -------------------------------------------------------------------------------- /include/MMUit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit.h" 12 | -------------------------------------------------------------------------------- /include/MMUit.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit.hpp" 12 | 13 | #include "Primitives.hpp" 14 | -------------------------------------------------------------------------------- /include/Primitives.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | class Primitives 14 | { 15 | public: 16 | Primitives() 17 | {} 18 | 19 | ~Primitives() 20 | {} 21 | 22 | // Read 23 | 24 | virtual uint8_t read8(virt_addr_t address) { assert(0); } 25 | virtual uint16_t read16(virt_addr_t address) { assert(0); } 26 | virtual uint32_t read32(virt_addr_t address) { assert(0); } 27 | virtual uint64_t read64(virt_addr_t address) { assert(0); } 28 | virtual uintptr_t readAddress(virt_addr_t address) { assert(0); } 29 | 30 | // Write 31 | 32 | virtual void write8(virt_addr_t address, uint8_t data) { assert(0); } 33 | virtual void write16(virt_addr_t address, uint16_t data) { assert(0); } 34 | virtual void write32(virt_addr_t address, uint32_t data) { assert(0); } 35 | virtual void write64(virt_addr_t address, uint64_t data) { assert(0); } 36 | virtual void writeAddress(virt_addr_t address, uintptr_t data) { assert(0); } 37 | 38 | // Function call 39 | 40 | virtual uintptr_t callFunction(virt_addr_t address) { assert(0); } 41 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0) { assert(0); } 42 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1) { assert(0); } 43 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2) { assert(0); } 44 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) { assert(0); } 45 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { assert(0); } 46 | virtual uintptr_t callFunction(virt_addr_t address, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) { assert(0); } 47 | 48 | // Memory allocation 49 | 50 | virtual virt_addr_t allocInPhysicalMemory(uint32_t size) { assert(0); } 51 | virtual bool deallocInPhysicalMemory(virt_addr_t address, uint32_t size) { assert(0); } 52 | 53 | // Memory copy 54 | 55 | virtual void copyInKernel(virt_addr_t dst, virt_addr_t src, uint32_t size) { assert(0); } 56 | 57 | // Virtual <-> Physical address conversion 58 | 59 | phys_addr_t virtualToPhysical (virt_addr_t address) { assert(0); } 60 | virt_addr_t physicalToVirtual (phys_addr_t address) { assert(0); } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /include/VMAKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit/VMAPlatform.h" 12 | #include "VMAKit/VMATypes.h" 13 | #include "VMAKit/TCR.h" 14 | 15 | #include "VMAKit/MMUConfig.h" 16 | #include "VMAKit/TTWalker.h" 17 | #include "VMAKit/PageRelocator.h" 18 | -------------------------------------------------------------------------------- /include/VMAKit.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAKit/VMAPlatform.hpp" 12 | #include "VMAKit/VMATypes.hpp" 13 | #include "VMAKit/VirtualAddress.hpp" 14 | #include "VMAKit/TCR.hpp" 15 | #include "VMAKit/TTEntry.hpp" 16 | 17 | #include "VMAKit/MMUConfig.hpp" 18 | #include "VMAKit/TTWalker.hpp" 19 | #include "VMAKit/PageRelocator.hpp" 20 | -------------------------------------------------------------------------------- /include/VMAKit/MMUConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TCR.h" 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #ifndef __cplusplus 20 | 21 | typedef struct 22 | { 23 | TTGranule granule; 24 | TTLevel initial_level; 25 | uint32_t region_size_offset; 26 | } MMUConfig; 27 | 28 | #endif 29 | 30 | typedef struct { 31 | MMUConfig mmu_config; 32 | virt_addr_t table_base; 33 | 34 | uintptr_t cb_user_data; 35 | 36 | // primitives 37 | uintptr_t (*read_address)(virt_addr_t address); 38 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 39 | 40 | // cpp object 41 | void* object; 42 | } mmuconfigparser; 43 | 44 | void mmuconfig_Init(mmuconfigparser* configparser); 45 | void mmuconfig_SetTCR_EL1(mmuconfigparser* configparser, tcr_el1_t tcr_value); 46 | MMUConfig mmuconfig_GetConfigFor(mmuconfigparser* configparser, ExceptionLevel el); 47 | void mmuconfig_Clear(mmuconfigparser* configparser); 48 | void mmuconfig_Close(mmuconfigparser* configparcer); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /include/VMAKit/MMUConfig.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | #include "TCR.hpp" 14 | 15 | struct MMUConfig 16 | { 17 | TTGranule granule; 18 | TTLevel initialLevel; 19 | uint32_t regionSizeOffset; 20 | }; 21 | 22 | class MMUConfigParser 23 | { 24 | public: 25 | MMUConfigParser() 26 | { 27 | clear(); 28 | }; 29 | ~MMUConfigParser() 30 | {}; 31 | 32 | void setTCR_EL1(TCR_EL1 tcr_el1) 33 | { 34 | // Parse TCR for TTBR0 35 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL0)]; 36 | 37 | config->regionSizeOffset = tcr_el1.getT0SZ(); 38 | switch (tcr_el1.getTG0()) { 39 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 40 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 41 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 42 | default: assert(0); 43 | } 44 | 45 | if (config->regionSizeOffset) { 46 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 47 | } 48 | 49 | // Parse TCR for TTBR1 50 | config = &m_configs[uint32_t(ExceptionLevel::EL1)]; 51 | 52 | config->regionSizeOffset = tcr_el1.getT1SZ(); 53 | switch (tcr_el1.getTG1()) { 54 | case TCR_TG1::Granule4K: config->granule = TTGranule::Granule4K; break; 55 | case TCR_TG1::Granule16K: config->granule = TTGranule::Granule16K; break; 56 | case TCR_TG1::Granule64K: config->granule = TTGranule::Granule64K; break; 57 | default: assert(0); 58 | } 59 | 60 | if (config->regionSizeOffset) { 61 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 62 | } 63 | } 64 | 65 | void setTCR_EL1(tcr_el1_t tcr_value) 66 | { 67 | setTCR_EL1(TCR_EL1(tcr_value)); 68 | } 69 | 70 | void setTCR_EL2(TCR_EL2 tcr_el2) 71 | { 72 | // Parse TCR for TTBR0 73 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL2)]; 74 | 75 | config->regionSizeOffset = tcr_el2.getT0SZ(); 76 | switch (tcr_el2.getTG0()) { 77 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 78 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 79 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 80 | default: assert(0); 81 | } 82 | 83 | if (config->regionSizeOffset) { 84 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 85 | } 86 | } 87 | 88 | void setTCR_EL2(tcr_el2_t tcr_value) 89 | { 90 | setTCR_EL2(TCR_EL2(tcr_value)); 91 | } 92 | 93 | void setTCR_EL3(TCR_EL3 tcr_el3) 94 | { 95 | // Parse TCR for TTBR0 96 | MMUConfig* config = &m_configs[uint32_t(ExceptionLevel::EL3)]; 97 | 98 | config->regionSizeOffset = tcr_el3.getT0SZ(); 99 | switch (tcr_el3.getTG0()) { 100 | case TCR_TG0::Granule4K: config->granule = TTGranule::Granule4K; break; 101 | case TCR_TG0::Granule16K: config->granule = TTGranule::Granule16K; break; 102 | case TCR_TG0::Granule64K: config->granule = TTGranule::Granule64K; break; 103 | default: assert(0); 104 | } 105 | 106 | if (config->regionSizeOffset) { 107 | config->initialLevel = getInitialLevel(config->granule, config->regionSizeOffset); 108 | } 109 | } 110 | 111 | void setTCR_EL3(tcr_el3_t tcr_value) 112 | { 113 | setTCR_EL3(TCR_EL3(tcr_value)); 114 | } 115 | 116 | MMUConfig getConfigFor(ExceptionLevel el) 117 | { 118 | return m_configs[uint32_t(el)]; 119 | } 120 | 121 | void clear() 122 | { 123 | for (uint32_t i = 0; i < uint32_t(ExceptionLevel::Count); i++) 124 | m_configs[i] = { .granule = TTGranule::Undefined, .initialLevel = TTLevel::Undefined, .regionSizeOffset = 0 }; 125 | } 126 | 127 | private: 128 | 129 | TTLevel getInitialLevel(TTGranule granule, uint32_t regionOffsetSize) 130 | { 131 | assert(regionOffsetSize >= 16 && regionOffsetSize <= 39); 132 | 133 | switch (granule) { 134 | case TTGranule::Granule4K: 135 | { 136 | // Table D4-11 TCR.TnSZ values and IA ranges, 4K granule with no concatenation of tables 137 | switch (regionOffsetSize) { 138 | case 16 ... 24: return TTLevel::Level0; 139 | case 25 ... 33: return TTLevel::Level1; 140 | case 34 ... 39: return TTLevel::Level2; 141 | default: break; 142 | }; 143 | break; 144 | } 145 | case TTGranule::Granule16K: 146 | { 147 | // Table D4-14 TCR.TnSZ values and IA ranges, 16K granule with no concatenation of tables 148 | switch (regionOffsetSize) { 149 | case 16: return TTLevel::Level0; 150 | case 17 ... 27: return TTLevel::Level1; 151 | case 28 ... 38: return TTLevel::Level2; 152 | case 39: return TTLevel::Level3; 153 | default: break; 154 | }; 155 | break; 156 | } 157 | case TTGranule::Granule64K: 158 | { 159 | // Table D4-17 TCR.TnSZ values and IA ranges, 64K granule with no concatenation of tables 160 | switch (regionOffsetSize) { 161 | case 16 ... 21: return TTLevel::Level1; 162 | case 22 ... 34: return TTLevel::Level2; 163 | case 35 ... 39: return TTLevel::Level3; 164 | default: break; 165 | } 166 | break; 167 | } 168 | 169 | default: assert(0); 170 | } 171 | 172 | return TTLevel::Undefined; 173 | } 174 | 175 | private: 176 | 177 | MMUConfig m_configs[uint32_t(ExceptionLevel::Count)]; 178 | }; 179 | -------------------------------------------------------------------------------- /include/VMAKit/PageRelocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TTEntry.h" 14 | #include "MMUConfig.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct 21 | { 22 | MMUConfig mmu_config; 23 | virt_addr_t table_base; 24 | 25 | uintptr_t cb_user_data; 26 | 27 | // primitives 28 | uintptr_t (*read_address)(virt_addr_t address); 29 | void (*write_address)(virt_addr_t address, virt_addr_t data); 30 | 31 | void (*copy_in_kernel)(virt_addr_t dst, virt_addr_t src, uint32_t size); 32 | 33 | virt_addr_t (*alloc_in_physical_memory)(uint32_t size); 34 | bool (*dealloc_in_physical_memory)(virt_addr_t address, uint32_t size); 35 | 36 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 37 | virt_addr_t (*virtual_to_physical)(phys_addr_t address); 38 | 39 | // cpp object 40 | void* object; 41 | } pagerelocator; 42 | 43 | typedef ttentry_t (*pagerelocator_callback)(TTLevel level, TTEntryDetails* oldEntry, TTEntryDetails* newEntry, uintptr_t user_data); 44 | 45 | void pagerelocator_Init(pagerelocator* relocator); 46 | bool pagerelocator_RelocatePage(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback); 47 | virt_addr_t pagerelocator_PreparePageRelocation(pagerelocator* relocator, virt_addr_t address, pagerelocator_callback callback); 48 | bool pagerelocator_CompleteRelocation(pagerelocator* relocator); 49 | bool pagerelocator_CancelRelocation(pagerelocator* relocator); 50 | bool pagerelocator_RestorePage(pagerelocator* relocator, virt_addr_t address); 51 | void pagerelocator_Close(pagerelocator* relocator); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /include/VMAKit/PageRelocator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "TTWalker.hpp" 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class PageRelocator : public PRIMITIVES 18 | { 19 | public: 20 | 21 | // Relocation callback is called when table translation entry is about to be updated 22 | using RelocatorCallback = std::function; 23 | static const RelocatorCallback DefaultCallback; 24 | 25 | public: 26 | 27 | PageRelocator() = delete; 28 | 29 | PageRelocator(MMUConfig mmuConfig, virt_addr_t tableBase) 30 | : m_mmuConfig(mmuConfig), m_tableBase(tableBase), 31 | kPageSize(uint32_t(mmuConfig.granule)), kPageMask(kPageSize - 1) 32 | {} 33 | 34 | bool isPageRelocatedFor(virt_addr_t address) 35 | { 36 | virt_addr_t targetPageAddress = address & ~kPageMask; 37 | 38 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) != m_relocatedPages.end()) 39 | return true; 40 | else 41 | return false; 42 | } 43 | 44 | bool isRelocationPendingFor(virt_addr_t address) 45 | { 46 | // check if there is a pending relocation 47 | if (m_relocationPending == false) 48 | return false; 49 | 50 | virt_addr_t targetPageAddress = address & ~kPageMask; 51 | 52 | // check if target page matches one in pending relocation 53 | if (m_stagingInfo.targetPageVA != targetPageAddress) 54 | return false; 55 | else 56 | return true; 57 | } 58 | 59 | bool relocatePageFor(virt_addr_t address, RelocatorCallback callback = DefaultCallback) 60 | { 61 | virt_addr_t page = preparePageRelocationFor(address, callback); 62 | if (page == kInvalidAddress) 63 | return false; 64 | 65 | return completeRelocation(); 66 | } 67 | 68 | virt_addr_t preparePageRelocationFor(virt_addr_t address, RelocatorCallback callback = DefaultCallback) 69 | { 70 | virt_addr_t targetPageAddress = address & ~kPageMask; 71 | 72 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) != m_relocatedPages.end()) 73 | return false; 74 | 75 | // cancel pending relocations 76 | cancelRelocation(); 77 | 78 | TTWalker walker(m_mmuConfig, m_tableBase); 79 | WalkResult result = walker.walkTo(address, [this, callback] (WalkPosition* position, TTGenericEntry* entry) { 80 | // safety checks 81 | if (position == nullptr || entry == nullptr) 82 | return WalkOperation::Stop; 83 | 84 | // get next level page 85 | virt_addr_t nextLevelPA = entry->getOutputAddress(); 86 | 87 | // check if page is already relocated 88 | if (m_relocationMap.find(nextLevelPA) != m_relocationMap.end()) 89 | { 90 | m_relocationMap[nextLevelPA].refCount++; 91 | return WalkOperation::Continue; 92 | } 93 | 94 | // allocate new page 95 | virt_addr_t newPageVA = this->allocInPhysicalMemory(kPageSize); 96 | assert((newPageVA & kPageMask) == 0); 97 | 98 | virt_addr_t nextLevelVA = this->physicalToVirtual(nextLevelPA); 99 | 100 | // clone page content 101 | this->copyInKernel(newPageVA, nextLevelVA, kPageSize); 102 | 103 | // get PA of allocated page 104 | phys_addr_t newPagePA = this->virtualToPhysical(newPageVA); 105 | 106 | // save original descriptor for current level 107 | ttentry_t oldEntryDescriptor = entry->getDescriptor(); 108 | 109 | ttentry_t newEntryDescriptor; 110 | TTGenericEntry* oldEntry = entry->clone(); 111 | 112 | // update entry PA 113 | entry->setOutputAddress(newPagePA); 114 | 115 | // apply external modifications 116 | newEntryDescriptor = callback(position->level, oldEntry, entry); 117 | delete oldEntry; 118 | 119 | Relocation relocation = { 120 | .originalEntry = oldEntryDescriptor, 121 | .allocatedPage = newPageVA, 122 | .refCount = 1 123 | }; 124 | 125 | if (entry->isPageDescriptor() == false) 126 | { 127 | // write TT entry back 128 | this->writeAddress(position->tableAddress + position->entryOffset, newEntryDescriptor); 129 | 130 | // save relocated page 131 | m_relocationMap[newPagePA] = relocation; 132 | } 133 | else 134 | { 135 | // stage relocation 136 | m_stagingInfo.allocatedPagePA = newPagePA; 137 | m_stagingInfo.allocatedPageEntry = newEntryDescriptor; 138 | m_stagingInfo.entryPosition = *position; 139 | m_stagingInfo.relocation = relocation; 140 | } 141 | 142 | return WalkOperation::Continue; 143 | }); 144 | 145 | if (result.getType() == WalkResultType::Complete) 146 | { 147 | m_stagingInfo.targetPageVA = targetPageAddress; 148 | m_relocationPending = true; 149 | 150 | return m_stagingInfo.relocation.allocatedPage; 151 | } 152 | else 153 | { 154 | restorePageFor(targetPageAddress); 155 | 156 | return kInvalidAddress; 157 | } 158 | } 159 | 160 | bool completeRelocation() 161 | { 162 | if (m_relocationPending == false) 163 | return false; 164 | 165 | // write TT entry back 166 | this->writeAddress(m_stagingInfo.entryPosition.tableAddress + m_stagingInfo.entryPosition.entryOffset, m_stagingInfo.allocatedPageEntry); 167 | 168 | // save relocated page 169 | m_relocationMap[m_stagingInfo.allocatedPagePA] = m_stagingInfo.relocation; 170 | 171 | // add page to relocated pages 172 | m_relocatedPages.push_back(m_stagingInfo.targetPageVA); 173 | 174 | m_relocationPending = false; 175 | 176 | return true; 177 | } 178 | 179 | bool cancelRelocation() 180 | { 181 | if (m_relocationPending == false) 182 | return false; 183 | 184 | // deallocate page 185 | this->deallocInPhysicalMemory(m_stagingInfo.relocation.allocatedPage, kPageSize); 186 | 187 | // restore TT entries 188 | bool result = restorePageFor(m_stagingInfo.targetPageVA); 189 | 190 | m_relocationPending = false; 191 | 192 | return result; 193 | } 194 | 195 | bool restorePageFor(virt_addr_t address) 196 | { 197 | virt_addr_t targetPageAddress = address & ~kPageMask; 198 | 199 | if (std::find(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress) == m_relocatedPages.end()) 200 | { 201 | // unable to find page, check if there is a pending relocation 202 | if (m_relocationPending == false) 203 | return false; 204 | 205 | // check if target page matches one in pending relocation 206 | if (m_stagingInfo.targetPageVA != targetPageAddress) 207 | return false; 208 | } 209 | 210 | TTWalker walker(m_mmuConfig, m_tableBase); 211 | 212 | bool result = walker.reverseWalkFrom(address, [this] (WalkPosition* position, TTGenericEntry* entry) { 213 | // safety checks 214 | if (position == nullptr || entry == nullptr) 215 | return WalkOperation::Stop; 216 | 217 | // get level page 218 | virt_addr_t levelPA = entry->getOutputAddress(); 219 | if (m_relocationMap.find(levelPA) == m_relocationMap.end()) 220 | return WalkOperation::Continue; 221 | 222 | // get original descriptor 223 | Relocation& relocation = m_relocationMap[levelPA]; 224 | 225 | if (relocation.refCount == 1) 226 | { 227 | // restore TT entry 228 | this->writeAddress(position->tableAddress + position->entryOffset, relocation.originalEntry); 229 | 230 | // deallocate page 231 | this->deallocInPhysicalMemory(relocation.allocatedPage, kPageSize); 232 | 233 | // remove page from relocation map 234 | m_relocationMap.erase(levelPA); 235 | } 236 | else 237 | { 238 | relocation.refCount--; 239 | } 240 | 241 | return WalkOperation::Continue; 242 | }); 243 | 244 | if (m_relocationPending == false) 245 | { 246 | // remove page from relocated pages 247 | m_relocatedPages.erase(std::remove(m_relocatedPages.begin(), m_relocatedPages.end(), targetPageAddress), m_relocatedPages.end()); 248 | } 249 | 250 | return result; 251 | } 252 | 253 | private: 254 | 255 | const uint32_t kPageSize; 256 | const virt_addr_t kPageMask; 257 | 258 | MMUConfig m_mmuConfig; 259 | virt_addr_t m_tableBase = kInvalidAddress; 260 | 261 | struct Relocation 262 | { 263 | ttentry_t originalEntry; 264 | virt_addr_t allocatedPage; 265 | uint32_t refCount; 266 | }; 267 | 268 | struct StagingInfo 269 | { 270 | virt_addr_t targetPageVA; 271 | phys_addr_t allocatedPagePA; 272 | ttentry_t allocatedPageEntry; 273 | WalkPosition entryPosition; 274 | Relocation relocation; 275 | }; 276 | 277 | bool m_relocationPending = false; 278 | StagingInfo m_stagingInfo; 279 | 280 | std::vector m_relocatedPages; 281 | std::map m_relocationMap; 282 | }; 283 | 284 | template 285 | const typename PageRelocator::RelocatorCallback PageRelocator::DefaultCallback = 286 | [] (TTLevel level, TTGenericEntry* entry) -> ttentry_t 287 | { 288 | assert(entry != nullptr); 289 | return entry->getDescriptor(); 290 | }; 291 | -------------------------------------------------------------------------------- /include/VMAKit/TCR.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #ifndef __cplusplus 12 | 13 | typedef uint64_t tcr_el1_t; 14 | typedef uint32_t tcr_el2_t; 15 | typedef uint32_t tcr_el3_t; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/VMAKit/TCR.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | 14 | enum class TCR_EPD0 : uint32_t // Translation table walk disable for translations using TTBR0_EL1. 15 | { 16 | PerformWalk = 0b0, // Perform translation table walks using TTBR0_EL1. 17 | FaultOnMiss = 0b1, // A TLB miss on an address that is translated using TTBR0_EL1 generates a Translation fault. No translation table walk is performed. 18 | }; 19 | 20 | enum class TCR_IRGN0 : uint32_t // Inner cacheability attribute for memory associated with translation table walks using TTBR0_EL1. 21 | { 22 | InnerNC = 0b00, // Normal memory, Inner Non-cacheable 23 | InnerWB_RA_WA_C = 0b01, // Normal memory, Inner Write-Back Read-Allocate Write-Allocate Cacheable 24 | InnerWT_RA_NWA_C = 0b10, // Normal memory, Inner Write-Through Read-Allocate No Write-Allocate Cacheable 25 | InnerWB_RA_NWA_C = 0b11, // Normal memory, Inner Write-Back Read-Allocate No Write-Allocate Cacheable 26 | }; 27 | 28 | enum class TCR_ORGN0 : uint32_t // Outer cacheability attribute for memory associated with translation table walks using TTBR0_EL1. 29 | 30 | { 31 | OuterNC = 0b00, // Normal memory, Outer Non-cacheable 32 | OuterWB_RA_WA_C = 0b01, // Normal memory, Outer Write-Back Read-Allocate Write-Allocate Cacheable 33 | OuterWT_RA_NWA_C = 0b10, // Normal memory, Outer Write-Through Read-Allocate No Write-Allocate Cacheable 34 | OuterWB_RA_NWA_C = 0b11, // Normal memory, Outer Write-Back Read-Allocate No Write-Allocate Cacheable 35 | }; 36 | 37 | enum class TCR_SH0 : uint32_t // Shareability attribute for memory associated with translation table walks using TTBR0_EL1. 38 | { 39 | NonShareable = 0b00, // Non-shareable 40 | OuterShareable = 0b10, // Outer Shareable 41 | InnerShareable = 0b11, // Inner Shareable 42 | }; 43 | 44 | enum class TCR_TG0 : uint32_t // Granule size for the TTBR0_EL1. 45 | { 46 | Granule4K = 0b00, // 4KB 47 | Granule64K = 0b01, // 64KB 48 | Granule16K = 0b10, // 16KB 49 | }; 50 | 51 | enum class TCR_A1 : uint32_t // Selects whether TTBR0_EL1 or TTBR1_EL1 defines the ASID. 52 | { 53 | ASID_TTBR0 = 0b0, // TTBR0_EL1.ASID defines the ASID. 54 | ASID_TTBR1 = 0b1, // TTBR1_EL1.ASID defines the ASID. 55 | }; 56 | 57 | enum class TCR_EPD1 : uint32_t // Translation table walk disable for translations using TTBR1_EL1. 58 | { 59 | PerformWalk = 0b0, // Perform translation table walks using TTBR1_EL1. 60 | FaultOnMiss = 0b1, // A TLB miss on an address that is translated using TTBR1_EL1 generates a Translation fault. No translation table walk is performed. 61 | }; 62 | 63 | enum class TCR_IRGN1 : uint32_t // Inner cacheability attribute for memory associated with translation table walks using TTBR1_EL1. 64 | { 65 | InnerNC = 0b00, // Normal memory, Inner Non-cacheable 66 | InnerWB_RA_WA_C = 0b01, // Normal memory, Inner Write-Back Read-Allocate Write-Allocate Cacheable 67 | InnerWT_RA_NWA_C = 0b10, // Normal memory, Inner Write-Through Read-Allocate No Write-Allocate Cacheable 68 | InnerWB_RA_NWA_C = 0b11, // Normal memory, Inner Write-Back Read-Allocate No Write-Allocate Cacheable 69 | }; 70 | 71 | enum class TCR_ORGN1 : uint32_t // Outer cacheability attribute for memory associated with translation table walks using TTBR1_EL1. 72 | { 73 | OuterNC = 0b00, // Normal memory, Outer Non-cacheable 74 | OuterWB_RA_WA_C = 0b01, // Normal memory, Outer Write-Back Read-Allocate Write-Allocate Cacheable 75 | OuterWT_RA_NWA_C = 0b10, // Normal memory, Outer Write-Through Read-Allocate No Write-Allocate Cacheable 76 | OuterWB_RA_NWA_C = 0b11, // Normal memory, Outer Write-Back Read-Allocate No Write-Allocate Cacheable 77 | }; 78 | 79 | enum class TCR_SH1 : uint32_t // Shareability attribute for memory associated with translation table walks using TTBR1_EL1. 80 | { 81 | NonShareable = 0b00, // Non-shareable 82 | OuterShareable = 0b10, // Outer Shareable 83 | InnerShareable = 0b11, // Inner Shareable 84 | }; 85 | 86 | enum class TCR_TG1 : uint32_t // Granule size for the TTBR1_EL1. 87 | { 88 | Granule16K = 0b01, // 16KB 89 | Granule4K = 0b10, // 4KB 90 | Granule64K = 0b11, // 64KB 91 | }; 92 | 93 | enum class TCR_PS : uint32_t // Intermediate Physical Address Size. 94 | { 95 | Size4GB = 0b000, // 32 bits, 4GB. 96 | Size64GB = 0b001, // 36 bits, 64GB. 97 | Size1TB = 0b010, // 40 bits, 1TB. 98 | Size4TB = 0b011, // 42 bits, 4TB. 99 | Size16TB = 0b100, // 44 bits, 16TB. 100 | Size256TB = 0b101, // 48 bits, 256TB. 101 | }; 102 | 103 | using TCR_IPS = TCR_PS; 104 | 105 | enum class TCR_AS : uint32_t // ASID Size 106 | { 107 | ASID_8bit = 0b0, // 8 bit 108 | ASID_16bit = 0b1, // 16 bit 109 | }; 110 | 111 | enum class TCR_TBI : uint32_t // Top Byte ignored, indicates whether the top byte of an address is used for address match for the TTBR0_ELx region 112 | { 113 | TopByteUsed = 0b0, // Top Byte used in the address calculation. 114 | TopByteIgnored = 0b1, // Top Byte ignored in the address calculation. 115 | }; 116 | 117 | template union TCRFormat {}; 118 | 119 | // MARK: - TCR_EL1 120 | // D7.2.84. TCR_EL1, Translation Control Register (EL1) 121 | 122 | using tcr_el1_t = uint64_t; 123 | 124 | template<> 125 | union TCRFormat 126 | { 127 | using tcr_value_t = tcr_el1_t; 128 | 129 | struct 130 | { 131 | tcr_value_t T0SZ : 6; // [5:0] 132 | tcr_value_t RES0_1 : 1; // [6] 133 | TCR_EPD0 EPD0 : 1; // [7] 134 | TCR_IRGN0 IRGN0 : 2; // [9:8] 135 | TCR_ORGN0 ORGN0 : 2; // [11:10] 136 | TCR_SH0 SH0 : 2; // [13:12] 137 | TCR_TG0 TG0 : 2; // [15:14] 138 | tcr_value_t T1SZ : 6; // [21:16] 139 | TCR_A1 A1 : 1; // [22] 140 | TCR_EPD1 EPD1 : 1; // [23] 141 | TCR_IRGN1 IRGN1 : 2; // [25:24] 142 | TCR_ORGN1 ORGN1 : 2; // [27:26] 143 | TCR_SH1 SH1 : 2; // [29:28] 144 | TCR_TG1 TG1 : 2; // [31:30] 145 | TCR_IPS IPS : 3; // [34:32] 146 | tcr_value_t RES0_2 : 1; // [35] 147 | TCR_AS AS : 1; // [36] 148 | TCR_TBI TBI0 : 1; // [37] 149 | TCR_TBI TBI1 : 1; // [38] 150 | tcr_value_t RES0_3 : 25; // [63:39] 151 | } details; 152 | 153 | tcr_value_t value; 154 | 155 | TCRFormat(tcr_value_t value) : value(value) { } 156 | 157 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 158 | TCR_EPD0 getEPD0() { return details.EPD0; } 159 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 160 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 161 | TCR_SH0 getSH0() { return details.SH0; } 162 | TCR_TG0 getTG0() { return details.TG0; } 163 | uint32_t getT1SZ() { return uint32_t(details.T1SZ); } 164 | TCR_A1 getA1() { return details.A1; } 165 | TCR_EPD1 getEPD1() { return details.EPD1; } 166 | TCR_IRGN1 getIRGN1() { return details.IRGN1; } 167 | TCR_ORGN1 getORGN1() { return details.ORGN1; } 168 | TCR_SH1 getSH1() { return details.SH1; } 169 | TCR_TG1 getTG1() { return details.TG1; } 170 | TCR_IPS getIPS() { return details.IPS; } 171 | TCR_AS getAS() { return details.AS; } 172 | TCR_TBI getTBI0() { return details.TBI0; } 173 | TCR_TBI getTBI1() { return details.TBI1; } 174 | }; 175 | 176 | // MARK: - TCR_EL2 177 | // D7.2.85. TCR_EL2, Translation Control Register (EL2) 178 | 179 | using tcr_el2_t = uint32_t; 180 | 181 | template<> 182 | union TCRFormat 183 | { 184 | using tcr_value_t = tcr_el2_t; 185 | 186 | struct 187 | { 188 | tcr_value_t T0SZ : 6; // [5:0] 189 | tcr_value_t RES0_1 : 2; // [7:6] 190 | TCR_IRGN0 IRGN0 : 2; // [9:8] 191 | TCR_ORGN0 ORGN0 : 2; // [11:10] 192 | TCR_SH0 SH0 : 2; // [13:12] 193 | TCR_TG0 TG0 : 2; // [15:14] 194 | TCR_PS PS : 3; // [18:16] 195 | tcr_value_t RES0_2 : 1; // [19] 196 | TCR_TBI TBI : 1; // [20] 197 | tcr_value_t RES0_3 : 2; // [22:21] 198 | tcr_value_t RES1_1 : 1; // [23] 199 | tcr_value_t RES0_4 : 7; // [30:24] 200 | tcr_value_t RES1_2 : 1; // [31] 201 | } details; 202 | 203 | tcr_value_t value; 204 | 205 | TCRFormat(tcr_value_t value) : value(value) { } 206 | 207 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 208 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 209 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 210 | TCR_SH0 getSH0() { return details.SH0; } 211 | TCR_TG0 getTG0() { return details.TG0; } 212 | TCR_PS getPS() { return details.PS; } 213 | TCR_TBI getTBI() { return details.TBI; } 214 | }; 215 | 216 | // MARK: - TCR_EL3 217 | // D7.2.85. TCR_EL3, Translation Control Register (EL3) 218 | 219 | using tcr_el3_t = uint32_t; 220 | 221 | template<> 222 | union TCRFormat 223 | { 224 | using tcr_value_t = tcr_el3_t; 225 | 226 | struct 227 | { 228 | tcr_value_t T0SZ : 6; // [5:0] 229 | tcr_value_t RES0_1 : 2; // [7:6] 230 | TCR_IRGN0 IRGN0 : 2; // [9:8] 231 | TCR_ORGN0 ORGN0 : 2; // [11:10] 232 | TCR_SH0 SH0 : 2; // [13:12] 233 | TCR_TG0 TG0 : 2; // [15:14] 234 | TCR_PS PS : 3; // [18:16] 235 | tcr_value_t RES0_2 : 1; // [19] 236 | TCR_TBI TBI : 1; // [20] 237 | tcr_value_t RES0_3 : 2; // [22:21] 238 | tcr_value_t RES1_1 : 1; // [23] 239 | tcr_value_t RES0_4 : 7; // [30:24] 240 | tcr_value_t RES1_2 : 1; // [31] 241 | } details; 242 | 243 | tcr_value_t value; 244 | 245 | TCRFormat(tcr_value_t value) : value(value) { } 246 | 247 | uint32_t getT0SZ() { return uint32_t(details.T0SZ); } 248 | TCR_IRGN0 getIRGN0() { return details.IRGN0; } 249 | TCR_ORGN0 getORGN0() { return details.ORGN0; } 250 | TCR_SH0 getSH0() { return details.SH0; } 251 | TCR_TG0 getTG0() { return details.TG0; } 252 | TCR_PS getPS() { return details.PS; } 253 | TCR_TBI getTBI() { return details.TBI; } 254 | }; 255 | 256 | // MARK: - TCR 257 | 258 | template 259 | class TCR 260 | { 261 | public: 262 | 263 | using tcr_value_t = typename TCRFormat::tcr_value_t; 264 | 265 | TCR() : m_tcr(0) 266 | {} 267 | 268 | TCR(tcr_value_t value) : m_tcr(value) 269 | {} 270 | 271 | ~TCR() 272 | {}; 273 | 274 | tcr_value_t getValue() { return m_tcr.value; } 275 | 276 | uint32_t getT0SZ() { return m_tcr.getT0SZ(); } 277 | TCR_EPD0 getEPD0() { return m_tcr.getEPD0(); } 278 | TCR_IRGN0 getIRGN0() { return m_tcr.getIRGN0(); } 279 | TCR_ORGN0 getORGN0() { return m_tcr.getORGN0(); } 280 | TCR_SH0 getSH0() { return m_tcr.getSH0(); } 281 | TCR_TG0 getTG0() { return m_tcr.getTG0(); } 282 | uint32_t getT1SZ() { return m_tcr.getT1SZ(); } 283 | TCR_A1 getA1() { return m_tcr.getA1(); } 284 | TCR_EPD1 getEPD1() { return m_tcr.getEPD1(); } 285 | TCR_IRGN1 getIRGN1() { return m_tcr.getIRGN1(); } 286 | TCR_ORGN1 getORGN1() { return m_tcr.getORGN1(); } 287 | TCR_SH1 getSH1() { return m_tcr.getSH1(); } 288 | TCR_TG1 getTG1() { return m_tcr.getTG1(); } 289 | TCR_IPS getIPS() { return m_tcr.getIPS(); } 290 | TCR_AS getAS() { return m_tcr.getAS(); } 291 | TCR_TBI getTBI0() { return m_tcr.getTBI0(); } 292 | TCR_TBI getTBI1() { return m_tcr.getTBI1(); } 293 | 294 | TCR_PS getPS() { return m_tcr.getPS(); } 295 | TCR_TBI getTBI() { return m_tcr.getTBI(); } 296 | 297 | private: 298 | 299 | using TCRFormatType = TCRFormat; 300 | 301 | TCRFormatType m_tcr; 302 | }; 303 | 304 | // MARK: - TCR types 305 | 306 | using TCR_EL1 = TCR; 307 | using TCR_EL2 = TCR; 308 | using TCR_EL3 = TCR; 309 | 310 | -------------------------------------------------------------------------------- /include/VMAKit/TTEntry.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct 21 | { 22 | TTGranule granule; 23 | TTLevel level; 24 | ttentry_t descriptor; 25 | } TTEntryDetails; 26 | 27 | // Generic Entry 28 | static const ttentry_t kTTDescriptor_ValidBit = (1 << 0); 29 | static const ttentry_t kTTDescriptor_TableBit = (1 << 1); 30 | static const ttentry_t kTTDescriptor_PageBit = (1 << 1); 31 | 32 | // Table attributes 33 | static const ttentry_t kTTDescriptor_PXNTableBitShift = (59); 34 | static const ttentry_t kTTDescriptor_PXNTableBitMask = ((ttentry_t)1 << kTTDescriptor_PXNTableBitShift); 35 | static const ttentry_t kTTDescriptor_XNTableBitShift = (60); 36 | static const ttentry_t kTTDescriptor_XNTableBitMask = ((ttentry_t)1 << kTTDescriptor_XNTableBitShift); 37 | static const ttentry_t kTTDescriptor_APTableBitShift = (61); 38 | static const ttentry_t kTTDescriptor_APTableBitMask = ((ttentry_t)0b11 << kTTDescriptor_APTableBitShift); 39 | static const ttentry_t kTTDescriptor_NSTableBitShift = (63); 40 | static const ttentry_t kTTDescriptor_NSTableBitMask = ((ttentry_t)1 << kTTDescriptor_NSTableBitShift); 41 | 42 | typedef enum { 43 | kAPTable_NoEffect = 0b00, 44 | kAPTable_NoEL0ReadAccess = 0b01, 45 | kAPTable_NoAnyWriteAccess = 0b10, 46 | kAPTable_NoEL0ReadAndAnyWriteAccess = 0b11, 47 | } TTDescriptorAPTable; 48 | 49 | // Block and Page attributes 50 | 51 | static const ttentry_t kTTDescriptor_AttrIndxBitShift = (2); 52 | static const ttentry_t kTTDescriptor_AttrIndxBitMask = ((ttentry_t)0b111 << kTTDescriptor_AttrIndxBitShift); 53 | static const ttentry_t kTTDescriptor_NSBitShift = (5); 54 | static const ttentry_t kTTDescriptor_NSBitMask = ((ttentry_t)1 << kTTDescriptor_NSBitShift); 55 | static const ttentry_t kTTDescriptor_APBitShift = (6); 56 | static const ttentry_t kTTDescriptor_APBitMask = ((ttentry_t)0b11 << kTTDescriptor_APBitShift); 57 | static const ttentry_t kTTDescriptor_SHBitShift = (8); 58 | static const ttentry_t kTTDescriptor_SHBitMask = ((ttentry_t)0b11 << kTTDescriptor_SHBitShift); 59 | static const ttentry_t kTTDescriptor_AFBitShift = (10); 60 | static const ttentry_t kTTDescriptor_AFBitMask = ((ttentry_t)1 << kTTDescriptor_AFBitShift); 61 | static const ttentry_t kTTDescriptor_nGBitShift = (11); 62 | static const ttentry_t kTTDescriptor_nGBitMask = ((ttentry_t)1 << kTTDescriptor_nGBitShift); 63 | static const ttentry_t kTTDescriptor_ContiguousBitShift = (52); 64 | static const ttentry_t kTTDescriptor_ContiguousBitMask = ((ttentry_t)1 << kTTDescriptor_ContiguousBitShift); 65 | static const ttentry_t kTTDescriptor_PXNBitShift = (53); 66 | static const ttentry_t kTTDescriptor_PXNBitMask = ((ttentry_t)1 << kTTDescriptor_PXNBitShift); 67 | static const ttentry_t kTTDescriptor_XNBitShift = (54); 68 | static const ttentry_t kTTDescriptor_XNBitMask = ((ttentry_t)1 << kTTDescriptor_XNBitShift); 69 | 70 | typedef enum { 71 | kAP_HigherELReadWriteEL0None = 0b00, 72 | kAP_HigherELReadWriteEL0ReadWrite = 0b01, 73 | kAP_HigherELReadOnlyEL0None = 0b10, 74 | kAP_HigherELReadOnlyEL0ReadOnly = 0b11, 75 | 76 | } TTDescriptorAP; 77 | 78 | typedef enum { 79 | kSH_NonShareable = 0b00, 80 | kSH_Reserved = 0b01, 81 | kSH_OuterShareable = 0b10, 82 | kSH_InnerShareable = 0b11, 83 | } TTDescriptorSH; 84 | 85 | // Generic Entry 86 | 87 | bool ttentry_IsValid(TTEntryDetails* entry); 88 | bool ttentry_IsBlockDescriptor(TTEntryDetails* entry); 89 | bool ttentry_IsTableDescriptor(TTEntryDetails* entry); 90 | bool ttentry_IsReserved(TTEntryDetails* entry); 91 | bool ttentry_IsPageDescriptor(TTEntryDetails* entry); 92 | 93 | ttentry_t ttentry_GetDescriptor(TTEntryDetails* entry); 94 | void ttentry_SetDescriptor(TTEntryDetails* entry, ttentry_t descriptor); 95 | 96 | phys_addr_t ttentry_GetOutputAddress(TTEntryDetails* entry); 97 | void ttentry_SetOutputAddress(TTEntryDetails* entry, phys_addr_t address); 98 | 99 | // Table Entry 100 | 101 | bool ttentry_GetPXNTable(TTEntryDetails* entry); 102 | bool ttentry_GetXNTable(TTEntryDetails* entry); 103 | TTDescriptorAPTable ttentry_GetAPTable(TTEntryDetails* entry); 104 | bool ttentry_GetNSTable(TTEntryDetails* entry); 105 | 106 | void ttentry_SetPXNTable(TTEntryDetails* entry, bool value); 107 | void ttentry_SetXNTable(TTEntryDetails* entry, bool value); 108 | void ttentry_SetAPTable(TTEntryDetails* entry, TTDescriptorAPTable value); 109 | void ttentry_SetNSTable(TTEntryDetails* entry, bool value); 110 | 111 | // Block or Page Entry 112 | 113 | uint8_t ttentry_GetAttrIndx(TTEntryDetails* entry); 114 | bool ttentry_GetNS(TTEntryDetails* entry); 115 | TTDescriptorAP ttentry_GetAP(TTEntryDetails* entry); 116 | TTDescriptorSH ttentry_GetSH(TTEntryDetails* entry); 117 | bool ttentry_GetAF(TTEntryDetails* entry); 118 | bool ttentry_GetNG(TTEntryDetails* entry); 119 | bool ttentry_GetContiguous(TTEntryDetails* entry); 120 | bool ttentry_GetPXN(TTEntryDetails* entry); 121 | bool ttentry_GetXN(TTEntryDetails* entry); 122 | 123 | void ttentry_SetAttrIndx(TTEntryDetails* entry, uint8_t value); 124 | void ttentry_SetNS(TTEntryDetails* entry, bool value); 125 | void ttentry_SetAP(TTEntryDetails* entry, TTDescriptorAP value); 126 | void ttentry_SetSH(TTEntryDetails* entry, TTDescriptorSH value); 127 | void ttentry_SetAF(TTEntryDetails* entry, bool value); 128 | void ttentry_SetNG(TTEntryDetails* entry, bool value); 129 | void ttentry_SetContiguous(TTEntryDetails* entry, bool value); 130 | void ttentry_SetPXN(TTEntryDetails* entry, bool value); 131 | void ttentry_SetXN(TTEntryDetails* entry, bool value); 132 | 133 | #ifdef __cplusplus 134 | } 135 | #endif 136 | -------------------------------------------------------------------------------- /include/VMAKit/TTWalker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.h" 12 | #include "VMATypes.h" 13 | #include "TTEntry.h" 14 | #include "MMUConfig.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct { 21 | MMUConfig mmu_config; 22 | virt_addr_t table_base; 23 | 24 | uintptr_t cb_user_data; 25 | 26 | // primitives 27 | uintptr_t (*read_address)(virt_addr_t address); 28 | virt_addr_t (*physical_to_virtual)(phys_addr_t address); 29 | 30 | } ttwalker; 31 | 32 | #ifndef __cplusplus 33 | 34 | typedef enum { 35 | kWalkOperation_Stop = 0, 36 | kWalkOperation_Continue = 1 37 | } WalkOperation; 38 | 39 | typedef enum { 40 | kWalkResultType_Complete = 0, 41 | kWalkResultType_Stopped = 1, 42 | kWalkResultType_Failed = 2, 43 | kWalkResultType_Undefined 44 | } WalkResultType; 45 | 46 | typedef struct { 47 | TTLevel level; 48 | virt_addr_t tableAddress; 49 | offset_t entryOffset; 50 | } WalkPosition; 51 | 52 | typedef struct { 53 | WalkResultType type; 54 | TTLevel level; 55 | ttentry_t descriptor; 56 | phys_addr_t outputAddress; 57 | } WalkResult; 58 | 59 | #endif 60 | 61 | typedef WalkOperation (*ttwalker_callback)(WalkPosition* position, TTEntryDetails* entry, uintptr_t user_data); 62 | 63 | WalkResult ttwalker_Walk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback); 64 | bool ttwalker_ReverseWalk(ttwalker* walker, virt_addr_t address, ttwalker_callback callback); 65 | phys_addr_t ttwalker_FindPhysicalAddress(ttwalker* walker, virt_addr_t address); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | -------------------------------------------------------------------------------- /include/VMAKit/TTWalker.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "MMUConfig.hpp" 13 | #include 14 | 15 | enum class WalkOperation { 16 | Stop = false, 17 | Continue = true 18 | }; 19 | 20 | enum class WalkResultType { 21 | Complete = 0, 22 | Stopped = 1, 23 | Failed = 2, 24 | Undefined = 3 25 | }; 26 | 27 | struct WalkPosition { 28 | TTLevel level; 29 | virt_addr_t tableAddress; 30 | offset_t entryOffset; 31 | }; 32 | 33 | struct WalkResult { 34 | WalkResultType type; 35 | TTLevel level; 36 | ttentry_t descriptor; 37 | phys_addr_t outputAddress; 38 | 39 | WalkResultType getType() { return type; } 40 | TTLevel getLevel() { return level; } 41 | ttentry_t getDescriptor() { return descriptor; } 42 | phys_addr_t getOutputAddress() { return outputAddress; } 43 | 44 | WalkResult& setType(WalkResultType type) {this->type = type; return *this; } 45 | WalkResult& setLevel(TTLevel level) {this->level = level; return *this; } 46 | WalkResult& setDescriptor(ttentry_t descriptor) {this->descriptor = descriptor; return *this; } 47 | WalkResult& setOutputAddress(phys_addr_t address) {this->outputAddress = address; return *this; } 48 | }; 49 | 50 | class TTGenericWalker 51 | { 52 | public: 53 | 54 | // WalkerCallback callback is called for every level walker is going through 55 | using WalkerCallback = std::function; 56 | static const WalkerCallback DefaultCallback; 57 | 58 | public: 59 | 60 | virtual WalkResult walkTo(virt_addr_t address, WalkerCallback callback) = 0; 61 | virtual bool reverseWalkFrom(virt_addr_t address, WalkerCallback callback = DefaultCallback) = 0; 62 | virtual phys_addr_t findPhysicalAddress(virt_addr_t address) = 0; 63 | 64 | }; 65 | 66 | const TTGenericWalker::WalkerCallback TTGenericWalker::DefaultCallback = 67 | [] (WalkPosition* position, TTGenericEntry* entry) -> WalkOperation 68 | { 69 | return WalkOperation::Continue; 70 | }; 71 | 72 | template 73 | class TTWalker : public PRIMITIVES, public TTGenericWalker 74 | { 75 | public: 76 | 77 | TTWalker() = delete; 78 | 79 | TTWalker(MMUConfig mmuConfig, virt_addr_t tableBase) 80 | : m_mmuConfig(mmuConfig), m_tableBase(tableBase) 81 | {} 82 | 83 | WalkResult walkTo(virt_addr_t address, WalkerCallback callback = DefaultCallback) override 84 | { 85 | switch (m_mmuConfig.granule) { 86 | case TTGranule::Granule4K: return performWalkTo(address, callback); 87 | case TTGranule::Granule16K: return performWalkTo(address, callback); 88 | case TTGranule::Granule64K: return performWalkTo(address, callback); 89 | 90 | default: assert(0); 91 | } 92 | 93 | return WalkResult(); 94 | } 95 | 96 | bool reverseWalkFrom(virt_addr_t address, WalkerCallback callback) override 97 | { 98 | switch (m_mmuConfig.granule) { 99 | case TTGranule::Granule4K: return performReverseWalkFrom(address, callback); 100 | case TTGranule::Granule16K: return performReverseWalkFrom(address, callback); 101 | case TTGranule::Granule64K: return performReverseWalkFrom(address, callback); 102 | 103 | default: assert(0); 104 | } 105 | 106 | return false; 107 | } 108 | 109 | phys_addr_t findPhysicalAddress(virt_addr_t address) override 110 | { 111 | virt_addr_t pageMask = uint32_t(m_mmuConfig.granule) - 1; 112 | 113 | auto result = walkTo(address); 114 | if (result.getType() == WalkResultType::Complete) 115 | return result.getOutputAddress() | (address & pageMask); 116 | else 117 | return kInvalidAddress; 118 | } 119 | 120 | private: 121 | 122 | template 123 | WalkResult performWalkTo(virt_addr_t address, WalkerCallback callback = DefaultCallback) 124 | { 125 | WalkResult result; 126 | WalkPosition pos = { 127 | .level = m_mmuConfig.initialLevel, 128 | .tableAddress = m_tableBase, 129 | .entryOffset = 0 130 | }; 131 | 132 | VirtualAddress va(address, m_mmuConfig.regionSizeOffset); 133 | 134 | while (1) 135 | { 136 | result.level = pos.level; 137 | result.descriptor = 0; 138 | 139 | pos.entryOffset = va.getOffsetForLevel(pos.level); 140 | 141 | // get current table translation entry 142 | switch (pos.level) 143 | { 144 | case TTLevel::Level0: 145 | { 146 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 147 | result.descriptor = entry.getDescriptor(); 148 | 149 | // check is entry is valid 150 | if (entry.isValid() == false) 151 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 152 | 153 | // invalid if not table descriptor 154 | if (entry.isTableDescriptor() == false) 155 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 156 | 157 | // execute callback and interrupt walk if needed 158 | if (callback(&pos, &entry) == WalkOperation::Stop) 159 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 160 | 161 | // get next table address 162 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 163 | 164 | break; 165 | } 166 | case TTLevel::Level1: 167 | { 168 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 169 | result.descriptor = entry.getDescriptor(); 170 | 171 | // check is entry is valid 172 | if (entry.isValid() == false) 173 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 174 | 175 | // execute callback and interrupt walk if needed 176 | if (callback(&pos, &entry) == WalkOperation::Stop) 177 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 178 | 179 | // return block address if not table descriptor 180 | if (entry.isTableDescriptor() == false) 181 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 182 | 183 | // get next table address 184 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 185 | 186 | break; 187 | } 188 | case TTLevel::Level2: 189 | { 190 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 191 | result.descriptor = entry.getDescriptor(); 192 | 193 | // check is entry is valid 194 | if (entry.isValid() == false) 195 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 196 | 197 | // execute callback and interrupt walk if needed 198 | if (callback(&pos, &entry) == WalkOperation::Stop) 199 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 200 | 201 | // return block address if not table descriptor 202 | if (entry.isTableDescriptor() == false) 203 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 204 | 205 | // get next table address 206 | pos.tableAddress = this->physicalToVirtual(entry.getOutputAddress()); 207 | 208 | break; 209 | } 210 | case TTLevel::Level3: 211 | { 212 | auto entry = TTEntry(this->readAddress(pos.tableAddress + pos.entryOffset)); 213 | result.descriptor = entry.getDescriptor(); 214 | 215 | // check is entry is valid 216 | if (entry.isValid() == false) 217 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 218 | 219 | // invalid if not page descriptor 220 | if (entry.isPageDescriptor() == false) 221 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 222 | 223 | // execute callback and interrupt walk if needed 224 | if (callback(&pos, &entry) == WalkOperation::Stop) 225 | return result.setType(WalkResultType::Stopped).setOutputAddress(entry.getOutputAddress()); 226 | 227 | // return page address 228 | return result.setType(WalkResultType::Complete).setOutputAddress(entry.getOutputAddress()); 229 | } 230 | default: assert(0); 231 | } 232 | 233 | if (pos.tableAddress == kInvalidAddress) 234 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 235 | 236 | // switch to the next level 237 | pos.level++; 238 | } 239 | 240 | return result.setType(WalkResultType::Failed).setOutputAddress(kInvalidAddress); 241 | } 242 | 243 | template 244 | bool performReverseWalkFrom(virt_addr_t address, WalkerCallback callback) 245 | { 246 | struct ReverseWalk 247 | { 248 | uint32_t levels; 249 | WalkPosition position[uint32_t(TTLevel::Count)]; 250 | ttentry_t entry[uint32_t(TTLevel::Count)]; 251 | } walk; 252 | 253 | walk.levels = 0; 254 | 255 | // walk forward and save translation lookups 256 | TTWalker walker(m_mmuConfig, m_tableBase); 257 | WalkResult result = walker.walkTo(address, [&walk] (WalkPosition* position, TTGenericEntry* entry) { 258 | // safety checks 259 | if (position == nullptr || entry == nullptr) 260 | return WalkOperation::Stop; 261 | 262 | walk.position[walk.levels] = *position; 263 | walk.entry[walk.levels] = entry->getDescriptor(); 264 | walk.levels++; 265 | 266 | return WalkOperation::Continue; 267 | }); 268 | 269 | if (result.getType() == WalkResultType::Failed) 270 | return false; 271 | 272 | // walk backwards 273 | while (walk.levels != 0) 274 | { 275 | uint32_t currentLevel = walk.levels - 1; 276 | 277 | switch (walk.position[currentLevel].level) 278 | { 279 | case TTLevel::Level0: 280 | { 281 | auto entry = TTEntry(walk.entry[currentLevel]); 282 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 283 | return false; 284 | break; 285 | } 286 | case TTLevel::Level1: 287 | { 288 | auto entry = TTEntry(walk.entry[currentLevel]); 289 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 290 | return false; 291 | break; 292 | } 293 | case TTLevel::Level2: 294 | { 295 | auto entry = TTEntry(walk.entry[currentLevel]); 296 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 297 | return false; 298 | break; 299 | } 300 | case TTLevel::Level3: 301 | { 302 | auto entry = TTEntry(walk.entry[currentLevel]); 303 | if (callback(&(walk.position[currentLevel]), &entry) == WalkOperation::Stop) 304 | return false; 305 | break; 306 | } 307 | default: assert(0); 308 | } 309 | 310 | walk.levels--; 311 | } 312 | 313 | return true; 314 | } 315 | 316 | private: 317 | 318 | MMUConfig m_mmuConfig; 319 | virt_addr_t m_tableBase = kInvalidAddress; 320 | }; 321 | -------------------------------------------------------------------------------- /include/VMAKit/VMAPlatform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #ifndef __cplusplus 14 | typedef uint64_t phys_addr_t; 15 | typedef uint64_t virt_addr_t; 16 | typedef uint64_t offset_t; 17 | typedef uint64_t ttentry_t; 18 | 19 | static const uint32_t kPlatformAddressSize = sizeof(virt_addr_t); 20 | static const uint32_t kPlatformAddressBits = kPlatformAddressSize * 8; 21 | static const offset_t kInvalidAddress = -1; 22 | static const offset_t kInvalidAddressOffset = -1; 23 | #endif 24 | -------------------------------------------------------------------------------- /include/VMAKit/VMAPlatform.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | using phys_addr_t = uint64_t; 15 | using virt_addr_t = uint64_t; 16 | using offset_t = uint64_t; 17 | using ttentry_t = uint64_t; 18 | 19 | static const uint32_t kPlatformAddressSize = sizeof(virt_addr_t); 20 | static const uint32_t kPlatformAddressBits = kPlatformAddressSize * 8; 21 | static const offset_t kInvalidAddress = -1; 22 | static const offset_t kInvalidAddressOffset = -1; 23 | 24 | // Input address (IA) using the 4K translation granule 25 | // D4.2 The VMSAv8-64 address translation system (Figure D4-3) 26 | using vm_addr_4k = union { 27 | struct { 28 | virt_addr_t outputAddress : 12; // IA[11:0] 29 | virt_addr_t tableIndexL3 : 9; // IA[20:12] 30 | virt_addr_t tableIndexL2 : 9; // IA[29:21] 31 | virt_addr_t tableIndexL1 : 9; // IA[38:30] 32 | virt_addr_t tableIndexL0 : 9; // IA[47:39] 33 | virt_addr_t ttbrRange : 16; 34 | } details; 35 | 36 | virt_addr_t value; 37 | }; 38 | 39 | // Input address (IA) using the 16K translation granule 40 | // D4.2 The VMSAv8-64 address translation system (Figure D4-4) 41 | using vm_addr_16k = union { 42 | struct { 43 | virt_addr_t outputAddress : 14; // IA[13:0] 44 | virt_addr_t tableIndexL3 : 11; // IA[24:14] 45 | virt_addr_t tableIndexL2 : 11; // IA[35:25] 46 | virt_addr_t tableIndexL1 : 11; // IA[46:36] 47 | virt_addr_t tableIndexL0 : 1; // IA[47] 48 | virt_addr_t ttbrRange : 16; 49 | } details; 50 | 51 | virt_addr_t value; 52 | }; 53 | 54 | // Input address (IA) using the 16K translation granule 55 | // D4.2 The VMSAv8-64 address translation system (Figure D4-5) 56 | using vm_addr_64k = union { 57 | struct { 58 | virt_addr_t outputAddress : 16; // IA[15:0] 59 | virt_addr_t tableIndexL3 : 13; // IA[28:16] 60 | virt_addr_t tableIndexL2 : 13; // IA[41:29] 61 | virt_addr_t tableIndexL1 : 6; // IA[47:42] 62 | virt_addr_t ttbrRange : 16; 63 | } details; 64 | 65 | virt_addr_t value; 66 | }; 67 | 68 | 69 | -------------------------------------------------------------------------------- /include/VMAKit/VMATypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #ifndef __cplusplus 12 | 13 | typedef enum { 14 | kExceptionLevel0 = 0, 15 | kExceptionLevel1 = 1, 16 | kExceptionLevel2 = 2, 17 | kExceptionLevel3 = 3, 18 | kExceptionLevelCount, 19 | kExceptionLevelUndefined = -1, 20 | } ExceptionLevel; 21 | 22 | typedef enum { 23 | kTTLevel0 = 0, 24 | kTTLevel1 = 1, 25 | kTTLevel2 = 2, 26 | kTTLevel3 = 3, 27 | kTTLevelCount, 28 | kTTLevelUndefined = -1, 29 | } TTLevel; 30 | 31 | typedef enum { 32 | kTTGranule4K = 4 * 1024, 33 | kTTGranule16K = 16 * 1024, 34 | kTTGranule64K = 64 * 1024, 35 | kTTGranuleCount = 3, 36 | kTTGranuleUndefined = -1, 37 | } TTGranule; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/VMAKit/VMATypes.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | enum class ExceptionLevel { 12 | EL0 = 0, 13 | EL1 = 1, 14 | EL2 = 2, 15 | EL3 = 3, 16 | Count, 17 | Undefined = -1 18 | }; 19 | 20 | enum class TTLevel { 21 | Level0 = 0, 22 | Level1 = 1, 23 | Level2 = 2, 24 | Level3 = 3, 25 | Count, 26 | Undefined = -1 27 | }; 28 | 29 | TTLevel& operator++(TTLevel& level) 30 | { 31 | switch(level) 32 | { 33 | case TTLevel::Level0: return level = TTLevel::Level1; 34 | case TTLevel::Level1: return level = TTLevel::Level2; 35 | case TTLevel::Level2: return level = TTLevel::Level3; 36 | case TTLevel::Level3: return level = TTLevel::Level3; 37 | case TTLevel::Count: { assert(0); } 38 | case TTLevel::Undefined: { assert(0); } 39 | } 40 | }; 41 | 42 | TTLevel& operator++(TTLevel& level, int) 43 | { 44 | switch(level) 45 | { 46 | case TTLevel::Level0: return level = TTLevel::Level1; 47 | case TTLevel::Level1: return level = TTLevel::Level2; 48 | case TTLevel::Level2: return level = TTLevel::Level3; 49 | case TTLevel::Level3: return level = TTLevel::Level3; 50 | case TTLevel::Count: { assert(0); } 51 | case TTLevel::Undefined: { assert(0); } 52 | } 53 | }; 54 | 55 | TTLevel& operator--(TTLevel& level) 56 | { 57 | switch(level) 58 | { 59 | case TTLevel::Level0: return level = TTLevel::Level0; 60 | case TTLevel::Level1: return level = TTLevel::Level0; 61 | case TTLevel::Level2: return level = TTLevel::Level1; 62 | case TTLevel::Level3: return level = TTLevel::Level2; 63 | case TTLevel::Count: { assert(0); } 64 | case TTLevel::Undefined: { assert(0); } 65 | } 66 | }; 67 | 68 | TTLevel& operator--(TTLevel& level, int) 69 | { 70 | switch(level) 71 | { 72 | case TTLevel::Level0: return level = TTLevel::Level0; 73 | case TTLevel::Level1: return level = TTLevel::Level0; 74 | case TTLevel::Level2: return level = TTLevel::Level1; 75 | case TTLevel::Level3: return level = TTLevel::Level2; 76 | case TTLevel::Count: { assert(0); } 77 | case TTLevel::Undefined: { assert(0); } 78 | } 79 | }; 80 | 81 | enum class TTGranule { 82 | Granule4K = 4 * 1024, 83 | Granule16K = 16 * 1024, 84 | Granule64K = 64 * 1024, 85 | Count = 3, 86 | Undefined = -1 87 | }; 88 | -------------------------------------------------------------------------------- /include/VMAKit/VirtualAddress.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017, Alexander Hude 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD-style license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | // 8 | 9 | #pragma once 10 | 11 | #include "VMAPlatform.hpp" 12 | #include "VMATypes.hpp" 13 | 14 | template struct VirtualAddressType {}; 15 | 16 | template <> struct VirtualAddressType 17 | { 18 | using Type = vm_addr_4k; 19 | }; 20 | 21 | template <> struct VirtualAddressType 22 | { 23 | using Type = vm_addr_16k; 24 | }; 25 | 26 | template <> struct VirtualAddressType 27 | { 28 | using Type = vm_addr_64k; 29 | }; 30 | 31 | class GenericVirtualAddress 32 | { 33 | virtual offset_t getOffsetForLevel(TTLevel level) = 0; 34 | virtual offset_t getOffsetForLevel(uint32_t level) = 0; 35 | }; 36 | 37 | template 38 | class VirtualAddress : public GenericVirtualAddress 39 | { 40 | public: 41 | 42 | using VirtualAddressType = typename VirtualAddressType::Type; 43 | 44 | VirtualAddress() = delete; 45 | 46 | VirtualAddress(VirtualAddressType address, uint32_t regionSizeOffset = 0) 47 | : m_virtAddress(address), m_regionSizeOffset(regionSizeOffset) 48 | { assert(regionSizeOffset < kPlatformAddressBits); } 49 | 50 | VirtualAddress(virt_addr_t address, uint32_t regionSizeOffset = 0) 51 | : m_virtAddress({.value = address}), m_regionSizeOffset(regionSizeOffset) 52 | { assert(regionSizeOffset < kPlatformAddressBits); } 53 | 54 | virt_addr_t rawValue() 55 | { 56 | return m_virtAddress.value; 57 | } 58 | 59 | VirtualAddressType virtualAddress() 60 | { 61 | return m_virtAddress; 62 | } 63 | 64 | uint32_t regionSizeOffset() 65 | { 66 | return m_regionSizeOffset; 67 | } 68 | 69 | offset_t getOffsetForLevel(TTLevel level) override 70 | { 71 | VirtualAddressType va({.value = m_virtAddress.value & ((virt_addr_t(1) << (kPlatformAddressBits - m_regionSizeOffset)) - 1)}); 72 | 73 | switch (level) 74 | { 75 | case TTLevel::Level0: return va.details.tableIndexL0 * kPlatformAddressSize; 76 | case TTLevel::Level1: return va.details.tableIndexL1 * kPlatformAddressSize; 77 | case TTLevel::Level2: return va.details.tableIndexL2 * kPlatformAddressSize; 78 | case TTLevel::Level3: return va.details.tableIndexL3 * kPlatformAddressSize; 79 | default: assert(0); 80 | } 81 | } 82 | 83 | offset_t getOffsetForLevel(uint32_t level) override 84 | { 85 | if (level < (uint32_t)TTLevel::Count) 86 | return getOffsetForLevel((TTLevel)level); 87 | else 88 | return kInvalidAddressOffset; 89 | } 90 | 91 | private: 92 | 93 | VirtualAddressType m_virtAddress; 94 | uint32_t m_regionSizeOffset; 95 | }; 96 | 97 | template <> 98 | offset_t VirtualAddress::getOffsetForLevel(TTLevel level) 99 | { 100 | assert(level != TTLevel::Level0); 101 | 102 | VirtualAddressType va({.value = m_virtAddress.value & ((virt_addr_t(1) << (kPlatformAddressBits - m_regionSizeOffset)) - 1) }); 103 | 104 | switch (level) 105 | { 106 | case TTLevel::Level1: return va.details.tableIndexL1 * kPlatformAddressSize; 107 | case TTLevel::Level2: return va.details.tableIndexL2 * kPlatformAddressSize; 108 | case TTLevel::Level3: return va.details.tableIndexL3 * kPlatformAddressSize; 109 | default: assert(0); 110 | } 111 | } 112 | 113 | using VA4K = VirtualAddress; 114 | using VA16K = VirtualAddress; 115 | using VA64K = VirtualAddress; 116 | 117 | -------------------------------------------------------------------------------- /lib/libMMUit.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexhude/MMUit/94ad551825df05cb22a6567396aa41aa50e1c55a/lib/libMMUit.a --------------------------------------------------------------------------------