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