// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pagetables // Visitor is a generic type. type Visitor interface { // visit is called on each PTE. The returned boolean indicates whether // the walk should continue. visit(start uintptr, pte *PTE, align uintptr) bool // requiresAlloc indicates that new entries should be allocated within // the walked range. requiresAlloc() bool // requiresSplit indicates that entries in the given range should be // split if they are huge or jumbo pages. requiresSplit() bool } // Walker walks page tables. type Walker struct { // pageTables are the tables to walk. pageTables *PageTables // Visitor is the set of arguments. visitor Visitor } // iterateRange iterates over all appropriate levels of page tables for the given range. // // If requiresAlloc is true, then Set _must_ be called on all given PTEs. The // exception is super pages. If a valid super page (huge or jumbo) cannot be // installed, then the walk will continue to individual entries. // // This algorithm will attempt to maximize the use of super/sect pages whenever // possible. Whether a super page is provided will be clear through the range // provided in the callback. // // Note that if requiresAlloc is true, then no gaps will be present. However, // if alloc is not set, then the iteration will likely be full of gaps. // // Note that this function should generally be avoided in favor of Map, Unmap, // etc. when not necessary. // // Precondition: start must be page-aligned. // Precondition: start must be less than end. // Precondition: If requiresAlloc is true, then start and end should not span // non-canonical ranges. If they do, a panic will result. // //go:nosplit func (w *Walker) iterateRange(start, end uintptr) { if start%pteSize != 0 { panic("unaligned start") } if end < start { panic("start > end") } if start < lowerTop { if end <= lowerTop { w.iterateRangeCanonical(start, end) } else if end > lowerTop && end <= upperBottom { if w.visitor.requiresAlloc() { panic("alloc spans non-canonical range") } w.iterateRangeCanonical(start, lowerTop) } else { if w.visitor.requiresAlloc() { panic("alloc spans non-canonical range") } if !w.iterateRangeCanonical(start, lowerTop) { return } w.iterateRangeCanonical(upperBottom, end) } } else if start < upperBottom { if end <= upperBottom { if w.visitor.requiresAlloc() { panic("alloc spans non-canonical range") } } else { if w.visitor.requiresAlloc() { panic("alloc spans non-canonical range") } w.iterateRangeCanonical(upperBottom, end) } } else { w.iterateRangeCanonical(start, end) } } // next returns the next address quantized by the given size. // //go:nosplit func next(start uintptr, size uintptr) uintptr { start &= ^(size - 1) start += size return start }