github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/ring0/pagetables/pcids.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 import ( 18 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 19 ) 20 21 // PCIDs is a simple PCID database. 22 // 23 // This is not protected by locks and is thus suitable for use only with a 24 // single CPU at a time. 25 type PCIDs struct { 26 // mu protects below. 27 mu sync.Mutex 28 29 // cache are the assigned page tables. 30 cache map[*PageTables]uint16 31 32 // avail are available PCIDs. 33 avail []uint16 34 } 35 36 // NewPCIDs returns a new PCID database. 37 // 38 // start is the first index to assign. Typically this will be one, as the zero 39 // pcid will always be flushed on transition (see pagetables_x86.go). This may 40 // be more than one if specific PCIDs are reserved. 41 // 42 // Nil is returned iff the start and size are out of range. 43 func NewPCIDs(start, size uint16) *PCIDs { 44 if start+uint16(size) > limitPCID { 45 return nil // See comment. 46 } 47 p := &PCIDs{ 48 cache: make(map[*PageTables]uint16), 49 } 50 for pcid := start; pcid < start+size; pcid++ { 51 p.avail = append(p.avail, pcid) 52 } 53 return p 54 } 55 56 // Assign assigns a PCID to the given PageTables. 57 // 58 // This may overwrite any previous assignment provided. If this in the case, 59 // true is returned to indicate that the PCID should be flushed. 60 func (p *PCIDs) Assign(pt *PageTables) (uint16, bool) { 61 p.mu.Lock() 62 if pcid, ok := p.cache[pt]; ok { 63 p.mu.Unlock() 64 return pcid, false // No flush. 65 } 66 67 // Is there something available? 68 if len(p.avail) > 0 { 69 pcid := p.avail[len(p.avail)-1] 70 p.avail = p.avail[:len(p.avail)-1] 71 p.cache[pt] = pcid 72 73 // We need to flush because while this is in the available 74 // pool, it may have been used previously. 75 p.mu.Unlock() 76 return pcid, true 77 } 78 79 // Evict an existing table. 80 for old, pcid := range p.cache { 81 delete(p.cache, old) 82 p.cache[pt] = pcid 83 84 // A flush is definitely required in this case, these page 85 // tables may still be active. (They will just be assigned some 86 // other PCID if and when they hit the given CPU again.) 87 p.mu.Unlock() 88 return pcid, true 89 } 90 91 // No PCID. 92 p.mu.Unlock() 93 return 0, false 94 } 95 96 // Drop drops references to a set of page tables. 97 func (p *PCIDs) Drop(pt *PageTables) { 98 p.mu.Lock() 99 if pcid, ok := p.cache[pt]; ok { 100 delete(p.cache, pt) 101 p.avail = append(p.avail, pcid) 102 } 103 p.mu.Unlock() 104 }