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