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