github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/walker_arm64.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build arm64 16 17 package pagetables 18 19 // iterateRangeCanonical walks a canonical range. 20 // 21 //go:nosplit 22 func (w *Walker) iterateRangeCanonical(start, end uintptr) bool { 23 pgdEntryIndex := w.pageTables.root 24 if start >= upperBottom { 25 pgdEntryIndex = w.pageTables.archPageTables.root 26 } 27 28 for pgdIndex := (uint16((start & pgdMask) >> pgdShift)); start < end && pgdIndex < entriesPerPage; pgdIndex++ { 29 var ( 30 pgdEntry = &pgdEntryIndex[pgdIndex] 31 pudEntries *PTEs 32 ) 33 if !pgdEntry.Valid() { 34 if !w.visitor.requiresAlloc() { 35 // Skip over this entry. 36 start = next(start, pgdSize) 37 continue 38 } 39 40 // Allocate a new pgd. 41 pudEntries = w.pageTables.Allocator.NewPTEs() 42 pgdEntry.setPageTable(w.pageTables, pudEntries) 43 } else { 44 pudEntries = w.pageTables.Allocator.LookupPTEs(pgdEntry.Address()) 45 } 46 47 // Map the next level. 48 clearPUDEntries := uint16(0) 49 50 for pudIndex := uint16((start & pudMask) >> pudShift); start < end && pudIndex < entriesPerPage; pudIndex++ { 51 var ( 52 pudEntry = &pudEntries[pudIndex] 53 pmdEntries *PTEs 54 ) 55 if !pudEntry.Valid() { 56 if !w.visitor.requiresAlloc() { 57 // Skip over this entry. 58 clearPUDEntries++ 59 start = next(start, pudSize) 60 continue 61 } 62 63 // This level has 1-GB sect pages. Is this 64 // entire region at least as large as a single 65 // PUD entry? If so, we can skip allocating a 66 // new page for the pmd. 67 if start&(pudSize-1) == 0 && end-start >= pudSize { 68 pudEntry.SetSect() 69 if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) { 70 return false 71 } 72 if pudEntry.Valid() { 73 start = next(start, pudSize) 74 continue 75 } 76 } 77 78 // Allocate a new pud. 79 pmdEntries = w.pageTables.Allocator.NewPTEs() 80 pudEntry.setPageTable(w.pageTables, pmdEntries) 81 82 } else if pudEntry.IsSect() { 83 // Does this page need to be split? 84 if w.visitor.requiresSplit() && (start&(pudSize-1) != 0 || end < next(start, pudSize)) { 85 // Install the relevant entries. 86 pmdEntries = w.pageTables.Allocator.NewPTEs() 87 for index := uint16(0); index < entriesPerPage; index++ { 88 pmdEntries[index].SetSect() 89 pmdEntries[index].Set( 90 pudEntry.Address()+(pmdSize*uintptr(index)), 91 pudEntry.Opts()) 92 } 93 pudEntry.setPageTable(w.pageTables, pmdEntries) 94 } else { 95 // A sect page to be checked directly. 96 if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) { 97 return false 98 } 99 100 // Might have been cleared. 101 if !pudEntry.Valid() { 102 clearPUDEntries++ 103 } 104 105 // Note that the sect page was changed. 106 start = next(start, pudSize) 107 continue 108 } 109 110 } else { 111 pmdEntries = w.pageTables.Allocator.LookupPTEs(pudEntry.Address()) 112 } 113 114 // Map the next level, since this is valid. 115 clearPMDEntries := uint16(0) 116 117 for pmdIndex := uint16((start & pmdMask) >> pmdShift); start < end && pmdIndex < entriesPerPage; pmdIndex++ { 118 var ( 119 pmdEntry = &pmdEntries[pmdIndex] 120 pteEntries *PTEs 121 ) 122 if !pmdEntry.Valid() { 123 if !w.visitor.requiresAlloc() { 124 // Skip over this entry. 125 clearPMDEntries++ 126 start = next(start, pmdSize) 127 continue 128 } 129 130 // This level has 2-MB huge pages. If this 131 // region is contined in a single PMD entry? 132 // As above, we can skip allocating a new page. 133 if start&(pmdSize-1) == 0 && end-start >= pmdSize { 134 pmdEntry.SetSect() 135 if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) { 136 return false 137 } 138 if pmdEntry.Valid() { 139 start = next(start, pmdSize) 140 continue 141 } 142 } 143 144 // Allocate a new pmd. 145 pteEntries = w.pageTables.Allocator.NewPTEs() 146 pmdEntry.setPageTable(w.pageTables, pteEntries) 147 148 } else if pmdEntry.IsSect() { 149 // Does this page need to be split? 150 if w.visitor.requiresSplit() && (start&(pmdSize-1) != 0 || end < next(start, pmdSize)) { 151 // Install the relevant entries. 152 pteEntries = w.pageTables.Allocator.NewPTEs() 153 for index := uint16(0); index < entriesPerPage; index++ { 154 pteEntries[index].Set( 155 pmdEntry.Address()+(pteSize*uintptr(index)), 156 pmdEntry.Opts()) 157 } 158 pmdEntry.setPageTable(w.pageTables, pteEntries) 159 } else { 160 // A huge page to be checked directly. 161 if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) { 162 return false 163 } 164 165 // Might have been cleared. 166 if !pmdEntry.Valid() { 167 clearPMDEntries++ 168 } 169 170 // Note that the huge page was changed. 171 start = next(start, pmdSize) 172 continue 173 } 174 175 } else { 176 pteEntries = w.pageTables.Allocator.LookupPTEs(pmdEntry.Address()) 177 } 178 179 // Map the next level, since this is valid. 180 clearPTEEntries := uint16(0) 181 182 for pteIndex := uint16((start & pteMask) >> pteShift); start < end && pteIndex < entriesPerPage; pteIndex++ { 183 var ( 184 pteEntry = &pteEntries[pteIndex] 185 ) 186 if !pteEntry.Valid() && !w.visitor.requiresAlloc() { 187 clearPTEEntries++ 188 start += pteSize 189 continue 190 } 191 192 // At this point, we are guaranteed that start%pteSize == 0. 193 if !w.visitor.visit(uintptr(start), pteEntry, pteSize-1) { 194 return false 195 } 196 if !pteEntry.Valid() { 197 if w.visitor.requiresAlloc() { 198 panic("PTE not set after iteration with requiresAlloc!") 199 } 200 clearPTEEntries++ 201 } 202 203 // Note that the pte was changed. 204 start += pteSize 205 continue 206 } 207 208 // Check if we no longer need this page. 209 if clearPTEEntries == entriesPerPage { 210 pmdEntry.Clear() 211 w.pageTables.Allocator.FreePTEs(pteEntries) 212 clearPMDEntries++ 213 } 214 } 215 216 // Check if we no longer need this page. 217 if clearPMDEntries == entriesPerPage { 218 pudEntry.Clear() 219 w.pageTables.Allocator.FreePTEs(pmdEntries) 220 clearPUDEntries++ 221 } 222 } 223 224 // Check if we no longer need this page. 225 if clearPUDEntries == entriesPerPage { 226 pgdEntry.Clear() 227 w.pageTables.Allocator.FreePTEs(pudEntries) 228 } 229 } 230 return true 231 }