github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/ring0/pagetables/pagetables.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 // Package pagetables provides a generic implementation of pagetables. 16 // 17 // The core functions must be safe to call from a nosplit context. Furthermore, 18 // this pagetables implementation goes to lengths to ensure that all functions 19 // are free from runtime allocation. Calls to NewPTEs/FreePTEs may be made 20 // during walks, but these can be cached elsewhere if required. 21 package pagetables 22 23 import ( 24 "github.com/SagerNet/gvisor/pkg/hostarch" 25 ) 26 27 // PageTables is a set of page tables. 28 type PageTables struct { 29 // Allocator is used to allocate nodes. 30 Allocator Allocator 31 32 // root is the pagetable root. 33 // 34 // For same archs such as amd64, the upper of the PTEs is cloned 35 // from and owned by upperSharedPageTables which are shared among 36 // many PageTables if upperSharedPageTables is not nil. 37 root *PTEs 38 39 // rootPhysical is the cached physical address of the root. 40 // 41 // This is saved only to prevent constant translation. 42 rootPhysical uintptr 43 44 // archPageTables includes architecture-specific features. 45 archPageTables 46 47 // upperSharedPageTables represents a read-only shared upper 48 // of the Pagetable. When it is not nil, the upper is not 49 // allowed to be modified. 50 upperSharedPageTables *PageTables 51 52 // upperStart is the start address of the upper portion that 53 // are shared from upperSharedPageTables 54 upperStart uintptr 55 56 // readOnlyShared indicates the Pagetables are read-only and 57 // own the ranges that are shared with other Pagetables. 58 readOnlyShared bool 59 } 60 61 // Init initializes a set of PageTables. 62 // 63 // +checkescape:hard,stack 64 //go:nosplit 65 func (p *PageTables) Init(allocator Allocator) { 66 p.Allocator = allocator 67 p.root = p.Allocator.NewPTEs() 68 p.rootPhysical = p.Allocator.PhysicalFor(p.root) 69 } 70 71 // NewWithUpper returns new PageTables. 72 // 73 // upperSharedPageTables are used for mapping the upper of addresses, 74 // starting at upperStart. These pageTables should not be touched (as 75 // invalidations may be incorrect) after they are passed as an 76 // upperSharedPageTables. Only when all dependent PageTables are gone 77 // may they be used. The intenteded use case is for kernel page tables, 78 // which are static and fixed. 79 // 80 // Precondition: upperStart must be between canonical ranges. 81 // Precondition: upperStart must be pgdSize aligned. 82 // precondition: upperSharedPageTables must be marked read-only shared. 83 func NewWithUpper(a Allocator, upperSharedPageTables *PageTables, upperStart uintptr) *PageTables { 84 p := new(PageTables) 85 p.Init(a) 86 87 if upperSharedPageTables != nil { 88 if !upperSharedPageTables.readOnlyShared { 89 panic("Only read-only shared pagetables can be used as upper") 90 } 91 p.upperSharedPageTables = upperSharedPageTables 92 p.upperStart = upperStart 93 } 94 95 p.InitArch(a) 96 return p 97 } 98 99 // New returns new PageTables. 100 func New(a Allocator) *PageTables { 101 return NewWithUpper(a, nil, 0) 102 } 103 104 // mapVisitor is used for map. 105 type mapVisitor struct { 106 target uintptr // Input. 107 physical uintptr // Input. 108 opts MapOpts // Input. 109 prev bool // Output. 110 } 111 112 // visit is used for map. 113 // 114 //go:nosplit 115 func (v *mapVisitor) visit(start uintptr, pte *PTE, align uintptr) bool { 116 p := v.physical + (start - uintptr(v.target)) 117 if pte.Valid() && (pte.Address() != p || pte.Opts() != v.opts) { 118 v.prev = true 119 } 120 if p&align != 0 { 121 // We will install entries at a smaller granulaity if we don't 122 // install a valid entry here, however we must zap any existing 123 // entry to ensure this happens. 124 pte.Clear() 125 return true 126 } 127 pte.Set(p, v.opts) 128 return true 129 } 130 131 //go:nosplit 132 func (*mapVisitor) requiresAlloc() bool { return true } 133 134 //go:nosplit 135 func (*mapVisitor) requiresSplit() bool { return true } 136 137 // Map installs a mapping with the given physical address. 138 // 139 // True is returned iff there was a previous mapping in the range. 140 // 141 // Precondition: addr & length must be page-aligned, their sum must not overflow. 142 // 143 // +checkescape:hard,stack 144 //go:nosplit 145 func (p *PageTables) Map(addr hostarch.Addr, length uintptr, opts MapOpts, physical uintptr) bool { 146 if p.readOnlyShared { 147 panic("Should not modify read-only shared pagetables.") 148 } 149 if uintptr(addr)+length < uintptr(addr) { 150 panic("addr & length overflow") 151 } 152 if p.upperSharedPageTables != nil { 153 // ignore change to the read-only upper shared portion. 154 if uintptr(addr) >= p.upperStart { 155 return false 156 } 157 if uintptr(addr)+length > p.upperStart { 158 length = p.upperStart - uintptr(addr) 159 } 160 } 161 w := mapWalker{ 162 pageTables: p, 163 visitor: mapVisitor{ 164 target: uintptr(addr), 165 physical: physical, 166 opts: opts, 167 }, 168 } 169 w.iterateRange(uintptr(addr), uintptr(addr)+length) 170 return w.visitor.prev 171 } 172 173 // unmapVisitor is used for unmap. 174 type unmapVisitor struct { 175 count int 176 } 177 178 //go:nosplit 179 func (*unmapVisitor) requiresAlloc() bool { return false } 180 181 //go:nosplit 182 func (*unmapVisitor) requiresSplit() bool { return true } 183 184 // visit unmaps the given entry. 185 // 186 //go:nosplit 187 func (v *unmapVisitor) visit(start uintptr, pte *PTE, align uintptr) bool { 188 pte.Clear() 189 v.count++ 190 return true 191 } 192 193 // Unmap unmaps the given range. 194 // 195 // True is returned iff there was a previous mapping in the range. 196 // 197 // Precondition: addr & length must be page-aligned, their sum must not overflow. 198 // 199 // +checkescape:hard,stack 200 //go:nosplit 201 func (p *PageTables) Unmap(addr hostarch.Addr, length uintptr) bool { 202 if p.readOnlyShared { 203 panic("Should not modify read-only shared pagetables.") 204 } 205 if uintptr(addr)+length < uintptr(addr) { 206 panic("addr & length overflow") 207 } 208 if p.upperSharedPageTables != nil { 209 // ignore change to the read-only upper shared portion. 210 if uintptr(addr) >= p.upperStart { 211 return false 212 } 213 if uintptr(addr)+length > p.upperStart { 214 length = p.upperStart - uintptr(addr) 215 } 216 } 217 w := unmapWalker{ 218 pageTables: p, 219 visitor: unmapVisitor{ 220 count: 0, 221 }, 222 } 223 w.iterateRange(uintptr(addr), uintptr(addr)+length) 224 return w.visitor.count > 0 225 } 226 227 // emptyVisitor is used for emptiness checks. 228 type emptyVisitor struct { 229 count int 230 } 231 232 //go:nosplit 233 func (*emptyVisitor) requiresAlloc() bool { return false } 234 235 //go:nosplit 236 func (*emptyVisitor) requiresSplit() bool { return false } 237 238 // visit unmaps the given entry. 239 // 240 //go:nosplit 241 func (v *emptyVisitor) visit(start uintptr, pte *PTE, align uintptr) bool { 242 v.count++ 243 return true 244 } 245 246 // IsEmpty checks if the given range is empty. 247 // 248 // Precondition: addr & length must be page-aligned. 249 // 250 // +checkescape:hard,stack 251 //go:nosplit 252 func (p *PageTables) IsEmpty(addr hostarch.Addr, length uintptr) bool { 253 w := emptyWalker{ 254 pageTables: p, 255 } 256 w.iterateRange(uintptr(addr), uintptr(addr)+length) 257 return w.visitor.count == 0 258 } 259 260 // lookupVisitor is used for lookup. 261 type lookupVisitor struct { 262 target uintptr // Input & Output. 263 findFirst bool // Input. 264 physical uintptr // Output. 265 size uintptr // Output. 266 opts MapOpts // Output. 267 } 268 269 // visit matches the given address. 270 // 271 //go:nosplit 272 func (v *lookupVisitor) visit(start uintptr, pte *PTE, align uintptr) bool { 273 if !pte.Valid() { 274 // If looking for the first, then we just keep iterating until 275 // we find a valid entry. 276 return v.findFirst 277 } 278 // Is this within the current range? 279 v.target = start 280 v.physical = pte.Address() 281 v.size = (align + 1) 282 v.opts = pte.Opts() 283 return false 284 } 285 286 //go:nosplit 287 func (*lookupVisitor) requiresAlloc() bool { return false } 288 289 //go:nosplit 290 func (*lookupVisitor) requiresSplit() bool { return false } 291 292 // Lookup returns the physical address for the given virtual address. 293 // 294 // If findFirst is true, then the next valid address after addr is returned. 295 // If findFirst is false, then only a mapping for addr will be returned. 296 // 297 // Note that if size is zero, then no matching entry was found. 298 // 299 // +checkescape:hard,stack 300 //go:nosplit 301 func (p *PageTables) Lookup(addr hostarch.Addr, findFirst bool) (virtual hostarch.Addr, physical, size uintptr, opts MapOpts) { 302 mask := uintptr(hostarch.PageSize - 1) 303 addr &^= hostarch.Addr(mask) 304 w := lookupWalker{ 305 pageTables: p, 306 visitor: lookupVisitor{ 307 target: uintptr(addr), 308 findFirst: findFirst, 309 }, 310 } 311 end := ^hostarch.Addr(0) &^ hostarch.Addr(mask) 312 if !findFirst { 313 end = addr + 1 314 } 315 w.iterateRange(uintptr(addr), uintptr(end)) 316 return hostarch.Addr(w.visitor.target), w.visitor.physical, w.visitor.size, w.visitor.opts 317 } 318 319 // MarkReadOnlyShared marks the pagetables read-only and can be shared. 320 // 321 // It is usually used on the pagetables that are used as the upper 322 func (p *PageTables) MarkReadOnlyShared() { 323 p.readOnlyShared = true 324 } 325 326 // PrefaultRootTable touches the root table page to be sure that its physical 327 // pages are mapped. 328 // 329 //go:nosplit 330 //go:noinline 331 func (p *PageTables) PrefaultRootTable() PTE { 332 return p.root[0] 333 }