github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/pagetables.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 provides a generic implementation of pagetables.
    16  //
    17  // The core functions must be safe to call from a nosplit context. Furthermore,
    18  // this pagetables implementation goes to lengths to ensure that all functions
    19  // are free from runtime allocation. Calls to NewPTEs/FreePTEs may be made
    20  // during walks, but these can be cached elsewhere if required.
    21  package pagetables
    22  
    23  import (
    24  	"github.com/SagerNet/gvisor/pkg/hostarch"
    25  )
    26  
    27  // PageTables is a set of page tables.
    28  type PageTables struct {
    29  	// Allocator is used to allocate nodes.
    30  	Allocator Allocator
    31  
    32  	// root is the pagetable root.
    33  	//
    34  	// For same archs such as amd64, the upper of the PTEs is cloned
    35  	// from and owned by upperSharedPageTables which are shared among
    36  	// many PageTables if upperSharedPageTables is not nil.
    37  	root *PTEs
    38  
    39  	// rootPhysical is the cached physical address of the root.
    40  	//
    41  	// This is saved only to prevent constant translation.
    42  	rootPhysical uintptr
    43  
    44  	// archPageTables includes architecture-specific features.
    45  	archPageTables
    46  
    47  	// upperSharedPageTables represents a read-only shared upper
    48  	// of the Pagetable. When it is not nil, the upper is not
    49  	// allowed to be modified.
    50  	upperSharedPageTables *PageTables
    51  
    52  	// upperStart is the start address of the upper portion that
    53  	// are shared from upperSharedPageTables
    54  	upperStart uintptr
    55  
    56  	// readOnlyShared indicates the Pagetables are read-only and
    57  	// own the ranges that are shared with other Pagetables.
    58  	readOnlyShared bool
    59  }
    60  
    61  // Init initializes a set of PageTables.
    62  //
    63  // +checkescape:hard,stack
    64  //go:nosplit
    65  func (p *PageTables) Init(allocator Allocator) {
    66  	p.Allocator = allocator
    67  	p.root = p.Allocator.NewPTEs()
    68  	p.rootPhysical = p.Allocator.PhysicalFor(p.root)
    69  }
    70  
    71  // NewWithUpper returns new PageTables.
    72  //
    73  // upperSharedPageTables are used for mapping the upper of addresses,
    74  // starting at upperStart. These pageTables should not be touched (as
    75  // invalidations may be incorrect) after they are passed as an
    76  // upperSharedPageTables. Only when all dependent PageTables are gone
    77  // may they be used. The intenteded use case is for kernel page tables,
    78  // which are static and fixed.
    79  //
    80  // Precondition: upperStart must be between canonical ranges.
    81  // Precondition: upperStart must be pgdSize aligned.
    82  // precondition: upperSharedPageTables must be marked read-only shared.
    83  func NewWithUpper(a Allocator, upperSharedPageTables *PageTables, upperStart uintptr) *PageTables {
    84  	p := new(PageTables)
    85  	p.Init(a)
    86  
    87  	if upperSharedPageTables != nil {
    88  		if !upperSharedPageTables.readOnlyShared {
    89  			panic("Only read-only shared pagetables can be used as upper")
    90  		}
    91  		p.upperSharedPageTables = upperSharedPageTables
    92  		p.upperStart = upperStart
    93  	}
    94  
    95  	p.InitArch(a)
    96  	return p
    97  }
    98  
    99  // New returns new PageTables.
   100  func New(a Allocator) *PageTables {
   101  	return NewWithUpper(a, nil, 0)
   102  }
   103  
   104  // mapVisitor is used for map.
   105  type mapVisitor struct {
   106  	target   uintptr // Input.
   107  	physical uintptr // Input.
   108  	opts     MapOpts // Input.
   109  	prev     bool    // Output.
   110  }
   111  
   112  // visit is used for map.
   113  //
   114  //go:nosplit
   115  func (v *mapVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
   116  	p := v.physical + (start - uintptr(v.target))
   117  	if pte.Valid() && (pte.Address() != p || pte.Opts() != v.opts) {
   118  		v.prev = true
   119  	}
   120  	if p&align != 0 {
   121  		// We will install entries at a smaller granulaity if we don't
   122  		// install a valid entry here, however we must zap any existing
   123  		// entry to ensure this happens.
   124  		pte.Clear()
   125  		return true
   126  	}
   127  	pte.Set(p, v.opts)
   128  	return true
   129  }
   130  
   131  //go:nosplit
   132  func (*mapVisitor) requiresAlloc() bool { return true }
   133  
   134  //go:nosplit
   135  func (*mapVisitor) requiresSplit() bool { return true }
   136  
   137  // Map installs a mapping with the given physical address.
   138  //
   139  // True is returned iff there was a previous mapping in the range.
   140  //
   141  // Precondition: addr & length must be page-aligned, their sum must not overflow.
   142  //
   143  // +checkescape:hard,stack
   144  //go:nosplit
   145  func (p *PageTables) Map(addr hostarch.Addr, length uintptr, opts MapOpts, physical uintptr) bool {
   146  	if p.readOnlyShared {
   147  		panic("Should not modify read-only shared pagetables.")
   148  	}
   149  	if uintptr(addr)+length < uintptr(addr) {
   150  		panic("addr & length overflow")
   151  	}
   152  	if p.upperSharedPageTables != nil {
   153  		// ignore change to the read-only upper shared portion.
   154  		if uintptr(addr) >= p.upperStart {
   155  			return false
   156  		}
   157  		if uintptr(addr)+length > p.upperStart {
   158  			length = p.upperStart - uintptr(addr)
   159  		}
   160  	}
   161  	w := mapWalker{
   162  		pageTables: p,
   163  		visitor: mapVisitor{
   164  			target:   uintptr(addr),
   165  			physical: physical,
   166  			opts:     opts,
   167  		},
   168  	}
   169  	w.iterateRange(uintptr(addr), uintptr(addr)+length)
   170  	return w.visitor.prev
   171  }
   172  
   173  // unmapVisitor is used for unmap.
   174  type unmapVisitor struct {
   175  	count int
   176  }
   177  
   178  //go:nosplit
   179  func (*unmapVisitor) requiresAlloc() bool { return false }
   180  
   181  //go:nosplit
   182  func (*unmapVisitor) requiresSplit() bool { return true }
   183  
   184  // visit unmaps the given entry.
   185  //
   186  //go:nosplit
   187  func (v *unmapVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
   188  	pte.Clear()
   189  	v.count++
   190  	return true
   191  }
   192  
   193  // Unmap unmaps the given range.
   194  //
   195  // True is returned iff there was a previous mapping in the range.
   196  //
   197  // Precondition: addr & length must be page-aligned, their sum must not overflow.
   198  //
   199  // +checkescape:hard,stack
   200  //go:nosplit
   201  func (p *PageTables) Unmap(addr hostarch.Addr, length uintptr) bool {
   202  	if p.readOnlyShared {
   203  		panic("Should not modify read-only shared pagetables.")
   204  	}
   205  	if uintptr(addr)+length < uintptr(addr) {
   206  		panic("addr & length overflow")
   207  	}
   208  	if p.upperSharedPageTables != nil {
   209  		// ignore change to the read-only upper shared portion.
   210  		if uintptr(addr) >= p.upperStart {
   211  			return false
   212  		}
   213  		if uintptr(addr)+length > p.upperStart {
   214  			length = p.upperStart - uintptr(addr)
   215  		}
   216  	}
   217  	w := unmapWalker{
   218  		pageTables: p,
   219  		visitor: unmapVisitor{
   220  			count: 0,
   221  		},
   222  	}
   223  	w.iterateRange(uintptr(addr), uintptr(addr)+length)
   224  	return w.visitor.count > 0
   225  }
   226  
   227  // emptyVisitor is used for emptiness checks.
   228  type emptyVisitor struct {
   229  	count int
   230  }
   231  
   232  //go:nosplit
   233  func (*emptyVisitor) requiresAlloc() bool { return false }
   234  
   235  //go:nosplit
   236  func (*emptyVisitor) requiresSplit() bool { return false }
   237  
   238  // visit unmaps the given entry.
   239  //
   240  //go:nosplit
   241  func (v *emptyVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
   242  	v.count++
   243  	return true
   244  }
   245  
   246  // IsEmpty checks if the given range is empty.
   247  //
   248  // Precondition: addr & length must be page-aligned.
   249  //
   250  // +checkescape:hard,stack
   251  //go:nosplit
   252  func (p *PageTables) IsEmpty(addr hostarch.Addr, length uintptr) bool {
   253  	w := emptyWalker{
   254  		pageTables: p,
   255  	}
   256  	w.iterateRange(uintptr(addr), uintptr(addr)+length)
   257  	return w.visitor.count == 0
   258  }
   259  
   260  // lookupVisitor is used for lookup.
   261  type lookupVisitor struct {
   262  	target    uintptr // Input & Output.
   263  	findFirst bool    // Input.
   264  	physical  uintptr // Output.
   265  	size      uintptr // Output.
   266  	opts      MapOpts // Output.
   267  }
   268  
   269  // visit matches the given address.
   270  //
   271  //go:nosplit
   272  func (v *lookupVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
   273  	if !pte.Valid() {
   274  		// If looking for the first, then we just keep iterating until
   275  		// we find a valid entry.
   276  		return v.findFirst
   277  	}
   278  	// Is this within the current range?
   279  	v.target = start
   280  	v.physical = pte.Address()
   281  	v.size = (align + 1)
   282  	v.opts = pte.Opts()
   283  	return false
   284  }
   285  
   286  //go:nosplit
   287  func (*lookupVisitor) requiresAlloc() bool { return false }
   288  
   289  //go:nosplit
   290  func (*lookupVisitor) requiresSplit() bool { return false }
   291  
   292  // Lookup returns the physical address for the given virtual address.
   293  //
   294  // If findFirst is true, then the next valid address after addr is returned.
   295  // If findFirst is false, then only a mapping for addr will be returned.
   296  //
   297  // Note that if size is zero, then no matching entry was found.
   298  //
   299  // +checkescape:hard,stack
   300  //go:nosplit
   301  func (p *PageTables) Lookup(addr hostarch.Addr, findFirst bool) (virtual hostarch.Addr, physical, size uintptr, opts MapOpts) {
   302  	mask := uintptr(hostarch.PageSize - 1)
   303  	addr &^= hostarch.Addr(mask)
   304  	w := lookupWalker{
   305  		pageTables: p,
   306  		visitor: lookupVisitor{
   307  			target:    uintptr(addr),
   308  			findFirst: findFirst,
   309  		},
   310  	}
   311  	end := ^hostarch.Addr(0) &^ hostarch.Addr(mask)
   312  	if !findFirst {
   313  		end = addr + 1
   314  	}
   315  	w.iterateRange(uintptr(addr), uintptr(end))
   316  	return hostarch.Addr(w.visitor.target), w.visitor.physical, w.visitor.size, w.visitor.opts
   317  }
   318  
   319  // MarkReadOnlyShared marks the pagetables read-only and can be shared.
   320  //
   321  // It is usually used on the pagetables that are used as the upper
   322  func (p *PageTables) MarkReadOnlyShared() {
   323  	p.readOnlyShared = true
   324  }
   325  
   326  // PrefaultRootTable touches the root table page to be sure that its physical
   327  // pages are mapped.
   328  //
   329  //go:nosplit
   330  //go:noinline
   331  func (p *PageTables) PrefaultRootTable() PTE {
   332  	return p.root[0]
   333  }