summaryrefslogtreecommitdiffhomepage
path: root/pkg/ring0/pagetables/pagetables_aarch64.go
blob: 163a3aea370b113effb9cad7bd69bb8684c6947e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build arm64

package pagetables

import (
	"sync/atomic"

	"gvisor.dev/gvisor/pkg/usermem"
)

// archPageTables is architecture-specific data.
type archPageTables struct {
	// root is the pagetable root for kernel space.
	root *PTEs

	// rootPhysical is the cached physical address of the root.
	//
	// This is saved only to prevent constant translation.
	rootPhysical uintptr

	asid uint16
}

// TTBR0_EL1 returns the translation table base register 0.
//
//go:nosplit
func (p *PageTables) TTBR0_EL1(noFlush bool, asid uint16) uint64 {
	return uint64(p.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
}

// TTBR1_EL1 returns the translation table base register 1.
//
//go:nosplit
func (p *PageTables) TTBR1_EL1(noFlush bool, asid uint16) uint64 {
	return uint64(p.archPageTables.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
}

// Bits in page table entries.
const (
	typeTable   = 0x3 << 0
	typeSect    = 0x1 << 0
	typePage    = 0x3 << 0
	pteValid    = 0x1 << 0
	pteTableBit = 0x1 << 1
	pteTypeMask = 0x3 << 0
	present     = pteValid | pteTableBit
	user        = 0x1 << 6 /* AP[1] */
	readOnly    = 0x1 << 7 /* AP[2] */
	accessed    = 0x1 << 10
	dbm         = 0x1 << 51
	writable    = dbm
	cont        = 0x1 << 52
	pxn         = 0x1 << 53
	xn          = 0x1 << 54
	dirty       = 0x1 << 55
	nG          = 0x1 << 11
	shared      = 0x3 << 8
)

const (
	mtDevicenGnRE = 0x1 << 2
	mtNormal      = 0x4 << 2
)

const (
	executeDisable = xn
	optionMask     = 0xfff | 0xffff<<48
	protDefault    = accessed | shared
)

// MapOpts are x86 options.
type MapOpts struct {
	// AccessType defines permissions.
	AccessType usermem.AccessType

	// Global indicates the page is globally accessible.
	Global bool

	// User indicates the page is a user page.
	User bool
}

// PTE is a page table entry.
type PTE uintptr

// Clear clears this PTE, including sect page information.
//
//go:nosplit
func (p *PTE) Clear() {
	atomic.StoreUintptr((*uintptr)(p), 0)
}

// Valid returns true iff this entry is valid.
//
//go:nosplit
func (p *PTE) Valid() bool {
	return atomic.LoadUintptr((*uintptr)(p))&present != 0
}

// Opts returns the PTE options.
//
// These are all options except Valid and Sect.
//
//go:nosplit
func (p *PTE) Opts() MapOpts {
	v := atomic.LoadUintptr((*uintptr)(p))

	return MapOpts{
		AccessType: usermem.AccessType{
			Read:    true,
			Write:   v&readOnly == 0,
			Execute: v&xn == 0,
		},
		Global: v&nG == 0,
		User:   v&user != 0,
	}
}

// SetSect sets this page as a sect page.
//
// The page must not be valid or a panic will result.
//
//go:nosplit
func (p *PTE) SetSect() {
	if p.Valid() {
		// This is not allowed.
		panic("SetSect called on valid page!")
	}
	atomic.StoreUintptr((*uintptr)(p), typeSect)
}

// IsSect returns true iff this page is a sect page.
//
//go:nosplit
func (p *PTE) IsSect() bool {
	return atomic.LoadUintptr((*uintptr)(p))&pteTypeMask == typeSect
}

// Set sets this PTE value.
//
// This does not change the sect page property.
//
//go:nosplit
func (p *PTE) Set(addr uintptr, opts MapOpts) {
	v := (addr &^ optionMask) | nG | readOnly | protDefault
	if p.IsSect() {
		// Note that this is inherited from the previous instance. Set
		// does not change the value of Sect. See above.
		v |= typeSect
	} else {
		v |= typePage
	}
	if !opts.AccessType.Any() {
		// Leave as non-valid if no access is available.
		v &^= pteValid
	}

	if opts.Global {
		v = v &^ nG
	}

	if opts.AccessType.Execute {
		v = v &^ executeDisable
	} else {
		v |= executeDisable
	}
	if opts.AccessType.Write {
		v = v &^ readOnly
	}

	if opts.User {
		v |= user
		v |= mtNormal
	} else {
		v = v &^ user
		v |= mtNormal
	}
	atomic.StoreUintptr((*uintptr)(p), v)
}

// setPageTable sets this PTE value and forces the write bit and sect bit to
// be cleared. This is used explicitly for breaking sect pages.
//
//go:nosplit
func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
	addr := pt.Allocator.PhysicalFor(ptes)
	if addr&^optionMask != addr {
		// This should never happen.
		panic("unaligned physical address!")
	}
	v := addr | typeTable | protDefault | mtNormal
	atomic.StoreUintptr((*uintptr)(p), v)
}

// Address extracts the address. This should only be used if Valid returns true.
//
//go:nosplit
func (p *PTE) Address() uintptr {
	return atomic.LoadUintptr((*uintptr)(p)) &^ optionMask
}