github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/pagetables_x86.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  // +build 386 amd64
    16  
    17  package pagetables
    18  
    19  import (
    20  	"sync/atomic"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/hostarch"
    23  )
    24  
    25  // archPageTables is architecture-specific data.
    26  type archPageTables struct {
    27  	// pcid is the value assigned by PCIDs.Assign.
    28  	//
    29  	// Note that zero is a valid PCID.
    30  	pcid uint16
    31  }
    32  
    33  // CR3 returns the CR3 value for these tables.
    34  //
    35  // This may be called in interrupt contexts. A PCID of zero always implies a
    36  // flush and should be passed when PCIDs are not enabled. See pcids_x86.go for
    37  // more information.
    38  //
    39  //go:nosplit
    40  func (p *PageTables) CR3(noFlush bool, pcid uint16) uint64 {
    41  	// Bit 63 is set to avoid flushing the PCID (per SDM 4.10.4.1).
    42  	const noFlushBit uint64 = 0x8000000000000000
    43  	if noFlush && pcid != 0 {
    44  		return noFlushBit | uint64(p.rootPhysical) | uint64(pcid)
    45  	}
    46  	return uint64(p.rootPhysical) | uint64(pcid)
    47  }
    48  
    49  // Bits in page table entries.
    50  const (
    51  	present      = 0x001
    52  	writable     = 0x002
    53  	user         = 0x004
    54  	writeThrough = 0x008
    55  	cacheDisable = 0x010
    56  	accessed     = 0x020
    57  	dirty        = 0x040
    58  	super        = 0x080
    59  	global       = 0x100
    60  	optionMask   = executeDisable | 0xfff
    61  )
    62  
    63  // MapOpts are x86 options.
    64  type MapOpts struct {
    65  	// AccessType defines permissions.
    66  	AccessType hostarch.AccessType
    67  
    68  	// Global indicates the page is globally accessible.
    69  	Global bool
    70  
    71  	// User indicates the page is a user page.
    72  	User bool
    73  }
    74  
    75  // PTE is a page table entry.
    76  type PTE uintptr
    77  
    78  // Clear clears this PTE, including super page information.
    79  //
    80  //go:nosplit
    81  func (p *PTE) Clear() {
    82  	atomic.StoreUintptr((*uintptr)(p), 0)
    83  }
    84  
    85  // Valid returns true iff this entry is valid.
    86  //
    87  //go:nosplit
    88  func (p *PTE) Valid() bool {
    89  	return atomic.LoadUintptr((*uintptr)(p))&present != 0
    90  }
    91  
    92  // Opts returns the PTE options.
    93  //
    94  // These are all options except Valid and Super.
    95  //
    96  //go:nosplit
    97  func (p *PTE) Opts() MapOpts {
    98  	v := atomic.LoadUintptr((*uintptr)(p))
    99  	return MapOpts{
   100  		AccessType: hostarch.AccessType{
   101  			Read:    v&present != 0,
   102  			Write:   v&writable != 0,
   103  			Execute: v&executeDisable == 0,
   104  		},
   105  		Global: v&global != 0,
   106  		User:   v&user != 0,
   107  	}
   108  }
   109  
   110  // SetSuper sets this page as a super page.
   111  //
   112  // The page must not be valid or a panic will result.
   113  //
   114  //go:nosplit
   115  func (p *PTE) SetSuper() {
   116  	if p.Valid() {
   117  		// This is not allowed.
   118  		panic("SetSuper called on valid page!")
   119  	}
   120  	atomic.StoreUintptr((*uintptr)(p), super)
   121  }
   122  
   123  // IsSuper returns true iff this page is a super page.
   124  //
   125  //go:nosplit
   126  func (p *PTE) IsSuper() bool {
   127  	return atomic.LoadUintptr((*uintptr)(p))&super != 0
   128  }
   129  
   130  // Set sets this PTE value.
   131  //
   132  // This does not change the super page property.
   133  //
   134  //go:nosplit
   135  func (p *PTE) Set(addr uintptr, opts MapOpts) {
   136  	if !opts.AccessType.Any() {
   137  		p.Clear()
   138  		return
   139  	}
   140  	v := (addr &^ optionMask)
   141  	if opts.AccessType.Any() {
   142  		v |= present | accessed
   143  	}
   144  	if opts.User {
   145  		v |= user
   146  	}
   147  	if opts.Global {
   148  		v |= global
   149  	}
   150  	if !opts.AccessType.Execute {
   151  		v |= executeDisable
   152  	}
   153  	if opts.AccessType.Write {
   154  		v |= writable | dirty
   155  	}
   156  	if p.IsSuper() {
   157  		// Note that this is inherited from the previous instance. Set
   158  		// does not change the value of Super. See above.
   159  		v |= super
   160  	}
   161  	atomic.StoreUintptr((*uintptr)(p), v)
   162  }
   163  
   164  // setPageTable sets this PTE value and forces the write bit and super bit to
   165  // be cleared. This is used explicitly for breaking super pages.
   166  //
   167  //go:nosplit
   168  func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
   169  	addr := pt.Allocator.PhysicalFor(ptes)
   170  	if addr&^optionMask != addr {
   171  		// This should never happen.
   172  		panic("unaligned physical address!")
   173  	}
   174  	v := addr | present | user | writable | accessed | dirty
   175  	atomic.StoreUintptr((*uintptr)(p), v)
   176  }
   177  
   178  // Address extracts the address. This should only be used if Valid returns true.
   179  //
   180  //go:nosplit
   181  func (p *PTE) Address() uintptr {
   182  	return atomic.LoadUintptr((*uintptr)(p)) &^ optionMask
   183  }