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