github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/ring0/pagetables/allocator.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 16 17 // Allocator is used to allocate and map PTEs. 18 // 19 // Note that allocators may be called concurrently. 20 type Allocator interface { 21 // NewPTEs returns a new set of PTEs and their physical address. 22 NewPTEs() *PTEs 23 24 // PhysicalFor gives the physical address for a set of PTEs. 25 PhysicalFor(ptes *PTEs) uintptr 26 27 // LookupPTEs looks up PTEs by physical address. 28 LookupPTEs(physical uintptr) *PTEs 29 30 // FreePTEs marks a set of PTEs a freed, although they may not be available 31 // for use again until Recycle is called, below. 32 FreePTEs(ptes *PTEs) 33 34 // Recycle makes freed PTEs available for use again. 35 Recycle() 36 } 37 38 // RuntimeAllocator is a trivial allocator. 39 type RuntimeAllocator struct { 40 // used is the set of PTEs that have been allocated. This includes any 41 // PTEs that may be in the pool below. PTEs are only freed from this 42 // map by the Drain call. 43 // 44 // This exists to prevent accidental garbage collection. 45 used map[*PTEs]struct{} 46 47 // pool is the set of free-to-use PTEs. 48 pool []*PTEs 49 50 // freed is the set of recently-freed PTEs. 51 freed []*PTEs 52 } 53 54 // NewRuntimeAllocator returns an allocator that uses runtime allocation. 55 func NewRuntimeAllocator() *RuntimeAllocator { 56 r := new(RuntimeAllocator) 57 r.Init() 58 return r 59 } 60 61 // Init initializes a RuntimeAllocator. 62 func (r *RuntimeAllocator) Init() { 63 r.used = make(map[*PTEs]struct{}) 64 } 65 66 // Recycle returns freed pages to the pool. 67 func (r *RuntimeAllocator) Recycle() { 68 r.pool = append(r.pool, r.freed...) 69 r.freed = r.freed[:0] 70 } 71 72 // Drain empties the pool. 73 func (r *RuntimeAllocator) Drain() { 74 r.Recycle() 75 for i, ptes := range r.pool { 76 // Zap the entry in the underlying array to ensure that it can 77 // be properly garbage collected. 78 r.pool[i] = nil 79 // Similarly, free the reference held by the used map (these 80 // also apply for the pool entries). 81 delete(r.used, ptes) 82 } 83 r.pool = r.pool[:0] 84 } 85 86 // NewPTEs implements Allocator.NewPTEs. 87 // 88 // Note that the "physical" address here is actually the virtual address of the 89 // PTEs structure. The entries are tracked only to avoid garbage collection. 90 // 91 // This is guaranteed not to split as long as the pool is sufficiently full. 92 // 93 //go:nosplit 94 func (r *RuntimeAllocator) NewPTEs() *PTEs { 95 // Pull from the pool if we can. 96 if len(r.pool) > 0 { 97 ptes := r.pool[len(r.pool)-1] 98 r.pool = r.pool[:len(r.pool)-1] 99 return ptes 100 } 101 102 // Allocate a new entry. 103 ptes := newAlignedPTEs() 104 r.used[ptes] = struct{}{} 105 return ptes 106 } 107 108 // PhysicalFor returns the physical address for the given PTEs. 109 // 110 //go:nosplit 111 func (r *RuntimeAllocator) PhysicalFor(ptes *PTEs) uintptr { 112 return physicalFor(ptes) 113 } 114 115 // LookupPTEs implements Allocator.LookupPTEs. 116 // 117 //go:nosplit 118 func (r *RuntimeAllocator) LookupPTEs(physical uintptr) *PTEs { 119 return fromPhysical(physical) 120 } 121 122 // FreePTEs implements Allocator.FreePTEs. 123 // 124 //go:nosplit 125 func (r *RuntimeAllocator) FreePTEs(ptes *PTEs) { 126 r.freed = append(r.freed, ptes) 127 }