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