gvisor/pkg/ring0/pagetables/walker_generic.go

111 lines
3.3 KiB
Go

// 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
}