github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/walker_generic.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 package pagetables 16 17 // Visitor is a generic type. 18 type Visitor interface { 19 // visit is called on each PTE. The returned boolean indicates whether 20 // the walk should continue. 21 visit(start uintptr, pte *PTE, align uintptr) bool 22 23 // requiresAlloc indicates that new entries should be allocated within 24 // the walked range. 25 requiresAlloc() bool 26 27 // requiresSplit indicates that entries in the given range should be 28 // split if they are huge or jumbo pages. 29 requiresSplit() bool 30 } 31 32 // Walker walks page tables. 33 type Walker struct { 34 // pageTables are the tables to walk. 35 pageTables *PageTables 36 37 // Visitor is the set of arguments. 38 visitor Visitor 39 } 40 41 // iterateRange iterates over all appropriate levels of page tables for the given range. 42 // 43 // If requiresAlloc is true, then Set _must_ be called on all given PTEs. The 44 // exception is super pages. If a valid super page (huge or jumbo) cannot be 45 // installed, then the walk will continue to individual entries. 46 // 47 // This algorithm will attempt to maximize the use of super/sect pages whenever 48 // possible. Whether a super page is provided will be clear through the range 49 // provided in the callback. 50 // 51 // Note that if requiresAlloc is true, then no gaps will be present. However, 52 // if alloc is not set, then the iteration will likely be full of gaps. 53 // 54 // Note that this function should generally be avoided in favor of Map, Unmap, 55 // etc. when not necessary. 56 // 57 // Precondition: start must be page-aligned. 58 // Precondition: start must be less than end. 59 // Precondition: If requiresAlloc is true, then start and end should not span 60 // non-canonical ranges. If they do, a panic will result. 61 // 62 //go:nosplit 63 func (w *Walker) iterateRange(start, end uintptr) { 64 if start%pteSize != 0 { 65 panic("unaligned start") 66 } 67 if end < start { 68 panic("start > end") 69 } 70 if start < lowerTop { 71 if end <= lowerTop { 72 w.iterateRangeCanonical(start, end) 73 } else if end > lowerTop && end <= upperBottom { 74 if w.visitor.requiresAlloc() { 75 panic("alloc spans non-canonical range") 76 } 77 w.iterateRangeCanonical(start, lowerTop) 78 } else { 79 if w.visitor.requiresAlloc() { 80 panic("alloc spans non-canonical range") 81 } 82 if !w.iterateRangeCanonical(start, lowerTop) { 83 return 84 } 85 w.iterateRangeCanonical(upperBottom, end) 86 } 87 } else if start < upperBottom { 88 if end <= upperBottom { 89 if w.visitor.requiresAlloc() { 90 panic("alloc spans non-canonical range") 91 } 92 } else { 93 if w.visitor.requiresAlloc() { 94 panic("alloc spans non-canonical range") 95 } 96 w.iterateRangeCanonical(upperBottom, end) 97 } 98 } else { 99 w.iterateRangeCanonical(start, end) 100 } 101 } 102 103 // next returns the next address quantized by the given size. 104 // 105 //go:nosplit 106 func next(start uintptr, size uintptr) uintptr { 107 start &= ^(size - 1) 108 start += size 109 return start 110 }