gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  }