github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/pagetables_aarch64.go (about)

     1  // Copyright 2019 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 arm64
    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  	// root is the pagetable root for kernel space.
    28  	root *PTEs
    29  
    30  	// rootPhysical is the cached physical address of the root.
    31  	//
    32  	// This is saved only to prevent constant translation.
    33  	rootPhysical uintptr
    34  
    35  	asid uint16
    36  }
    37  
    38  // TTBR0_EL1 returns the translation table base register 0.
    39  //
    40  //go:nosplit
    41  func (p *PageTables) TTBR0_EL1(noFlush bool, asid uint16) uint64 {
    42  	return uint64(p.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
    43  }
    44  
    45  // TTBR1_EL1 returns the translation table base register 1.
    46  //
    47  //go:nosplit
    48  func (p *PageTables) TTBR1_EL1(noFlush bool, asid uint16) uint64 {
    49  	return uint64(p.archPageTables.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
    50  }
    51  
    52  // Bits in page table entries.
    53  const (
    54  	typeTable   = 0x3 << 0
    55  	typeSect    = 0x1 << 0
    56  	typePage    = 0x3 << 0
    57  	pteValid    = 0x1 << 0
    58  	pteTableBit = 0x1 << 1
    59  	pteTypeMask = 0x3 << 0
    60  	present     = pteValid | pteTableBit
    61  	user        = 0x1 << 6 /* AP[1] */
    62  	readOnly    = 0x1 << 7 /* AP[2] */
    63  	accessed    = 0x1 << 10
    64  	dbm         = 0x1 << 51
    65  	writable    = dbm
    66  	cont        = 0x1 << 52
    67  	pxn         = 0x1 << 53
    68  	xn          = 0x1 << 54
    69  	dirty       = 0x1 << 55
    70  	nG          = 0x1 << 11
    71  	shared      = 0x3 << 8
    72  )
    73  
    74  const (
    75  	mtDevicenGnRE = 0x1 << 2
    76  	mtNormal      = 0x4 << 2
    77  )
    78  
    79  const (
    80  	executeDisable = xn
    81  	optionMask     = 0xfff | 0xffff<<48
    82  	protDefault    = accessed | shared
    83  )
    84  
    85  // MapOpts are x86 options.
    86  type MapOpts struct {
    87  	// AccessType defines permissions.
    88  	AccessType hostarch.AccessType
    89  
    90  	// Global indicates the page is globally accessible.
    91  	Global bool
    92  
    93  	// User indicates the page is a user page.
    94  	User bool
    95  }
    96  
    97  // PTE is a page table entry.
    98  type PTE uintptr
    99  
   100  // Clear clears this PTE, including sect page information.
   101  //
   102  //go:nosplit
   103  func (p *PTE) Clear() {
   104  	atomic.StoreUintptr((*uintptr)(p), 0)
   105  }
   106  
   107  // Valid returns true iff this entry is valid.
   108  //
   109  //go:nosplit
   110  func (p *PTE) Valid() bool {
   111  	return atomic.LoadUintptr((*uintptr)(p))&present != 0
   112  }
   113  
   114  // Opts returns the PTE options.
   115  //
   116  // These are all options except Valid and Sect.
   117  //
   118  //go:nosplit
   119  func (p *PTE) Opts() MapOpts {
   120  	v := atomic.LoadUintptr((*uintptr)(p))
   121  
   122  	return MapOpts{
   123  		AccessType: hostarch.AccessType{
   124  			Read:    true,
   125  			Write:   v&readOnly == 0,
   126  			Execute: v&xn == 0,
   127  		},
   128  		Global: v&nG == 0,
   129  		User:   v&user != 0,
   130  	}
   131  }
   132  
   133  // SetSect sets this page as a sect page.
   134  //
   135  // The page must not be valid or a panic will result.
   136  //
   137  //go:nosplit
   138  func (p *PTE) SetSect() {
   139  	if p.Valid() {
   140  		// This is not allowed.
   141  		panic("SetSect called on valid page!")
   142  	}
   143  	atomic.StoreUintptr((*uintptr)(p), typeSect)
   144  }
   145  
   146  // IsSect returns true iff this page is a sect page.
   147  //
   148  //go:nosplit
   149  func (p *PTE) IsSect() bool {
   150  	return atomic.LoadUintptr((*uintptr)(p))&pteTypeMask == typeSect
   151  }
   152  
   153  // Set sets this PTE value.
   154  //
   155  // This does not change the sect page property.
   156  //
   157  //go:nosplit
   158  func (p *PTE) Set(addr uintptr, opts MapOpts) {
   159  	v := (addr &^ optionMask) | nG | readOnly | protDefault
   160  	if p.IsSect() {
   161  		// Note that this is inherited from the previous instance. Set
   162  		// does not change the value of Sect. See above.
   163  		v |= typeSect
   164  	} else {
   165  		v |= typePage
   166  	}
   167  	if !opts.AccessType.Any() {
   168  		// Leave as non-valid if no access is available.
   169  		v &^= pteValid
   170  	}
   171  
   172  	if opts.Global {
   173  		v = v &^ nG
   174  	}
   175  
   176  	if opts.AccessType.Execute {
   177  		v = v &^ executeDisable
   178  	} else {
   179  		v |= executeDisable
   180  	}
   181  	if opts.AccessType.Write {
   182  		v = v &^ readOnly
   183  	}
   184  
   185  	if opts.User {
   186  		v |= user
   187  		v |= mtNormal
   188  	} else {
   189  		v = v &^ user
   190  		v |= mtNormal
   191  	}
   192  	atomic.StoreUintptr((*uintptr)(p), v)
   193  }
   194  
   195  // setPageTable sets this PTE value and forces the write bit and sect bit to
   196  // be cleared. This is used explicitly for breaking sect pages.
   197  //
   198  //go:nosplit
   199  func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
   200  	addr := pt.Allocator.PhysicalFor(ptes)
   201  	if addr&^optionMask != addr {
   202  		// This should never happen.
   203  		panic("unaligned physical address!")
   204  	}
   205  	v := addr | typeTable | protDefault | mtNormal
   206  	atomic.StoreUintptr((*uintptr)(p), v)
   207  }
   208  
   209  // Address extracts the address. This should only be used if Valid returns true.
   210  //
   211  //go:nosplit
   212  func (p *PTE) Address() uintptr {
   213  	return atomic.LoadUintptr((*uintptr)(p)) &^ optionMask
   214  }