github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/kvm/physical_map.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 kvm 16 17 import ( 18 "fmt" 19 "sort" 20 21 "golang.org/x/sys/unix" 22 "github.com/SagerNet/gvisor/pkg/hostarch" 23 "github.com/SagerNet/gvisor/pkg/log" 24 "github.com/SagerNet/gvisor/pkg/ring0" 25 ) 26 27 type region struct { 28 virtual uintptr 29 length uintptr 30 } 31 32 type physicalRegion struct { 33 region 34 physical uintptr 35 } 36 37 // physicalRegions contains a list of available physical regions. 38 // 39 // The physical value used in physicalRegions is a number indicating the 40 // physical offset, aligned appropriately and starting above reservedMemory. 41 var physicalRegions []physicalRegion 42 43 // fillAddressSpace fills the host address space with PROT_NONE mappings until 44 // we have a host address space size that is less than or equal to the physical 45 // address space. This allows us to have an injective host virtual to guest 46 // physical mapping. 47 // 48 // The excluded regions are returned. 49 func fillAddressSpace() (excludedRegions []region) { 50 // We can cut vSize in half, because the kernel will be using the top 51 // half and we ignore it while constructing mappings. It's as if we've 52 // already excluded half the possible addresses. 53 vSize := ring0.UserspaceSize 54 55 // We exclude reservedMemory below from our physical memory size, so it 56 // needs to be dropped here as well. Otherwise, we could end up with 57 // physical addresses that are beyond what is mapped. 58 pSize := uintptr(1) << ring0.PhysicalAddressBits() 59 pSize -= reservedMemory 60 61 // Add specifically excluded regions; see excludeVirtualRegion. 62 applyVirtualRegions(func(vr virtualRegion) { 63 if excludeVirtualRegion(vr) { 64 excludedRegions = append(excludedRegions, vr.region) 65 vSize -= vr.length 66 log.Infof("excluded: virtual [%x,%x)", vr.virtual, vr.virtual+vr.length) 67 } 68 }) 69 70 // Do we need any more work? 71 if vSize < pSize { 72 return excludedRegions 73 } 74 75 // Calculate the required space and fill it. 76 // 77 // Note carefully that we add faultBlockSize to required up front, and 78 // on each iteration of the loop below (i.e. each new physical region 79 // we define), we add faultBlockSize again. This is done because the 80 // computation of physical regions will ensure proper alignments with 81 // faultBlockSize, potentially causing up to faultBlockSize bytes in 82 // internal fragmentation for each physical region. So we need to 83 // account for this properly during allocation. 84 requiredAddr, ok := hostarch.Addr(vSize - pSize + faultBlockSize).RoundUp() 85 if !ok { 86 panic(fmt.Sprintf( 87 "overflow for vSize (%x) - pSize (%x) + faultBlockSize (%x)", 88 vSize, pSize, faultBlockSize)) 89 } 90 required := uintptr(requiredAddr) 91 current := required // Attempted mmap size. 92 for filled := uintptr(0); filled < required && current > 0; { 93 addr, _, errno := unix.RawSyscall6( 94 unix.SYS_MMAP, 95 0, // Suggested address. 96 current, 97 unix.PROT_NONE, 98 unix.MAP_ANONYMOUS|unix.MAP_PRIVATE|unix.MAP_NORESERVE, 99 0, 0) 100 if errno != 0 { 101 // Attempt half the size; overflow not possible. 102 currentAddr, _ := hostarch.Addr(current >> 1).RoundUp() 103 current = uintptr(currentAddr) 104 continue 105 } 106 // We filled a block. 107 filled += current 108 excludedRegions = append(excludedRegions, region{ 109 virtual: addr, 110 length: current, 111 }) 112 // See comment above. 113 if filled != required { 114 required += faultBlockSize 115 } 116 } 117 if current == 0 { 118 panic("filling address space failed") 119 } 120 sort.Slice(excludedRegions, func(i, j int) bool { 121 return excludedRegions[i].virtual < excludedRegions[j].virtual 122 }) 123 for _, r := range excludedRegions { 124 log.Infof("region: virtual [%x,%x)", r.virtual, r.virtual+r.length) 125 } 126 return excludedRegions 127 } 128 129 // computePhysicalRegions computes physical regions. 130 func computePhysicalRegions(excludedRegions []region) (physicalRegions []physicalRegion) { 131 physical := uintptr(reservedMemory) 132 addValidRegion := func(virtual, length uintptr) { 133 if length == 0 { 134 return 135 } 136 if virtual == 0 { 137 virtual += hostarch.PageSize 138 length -= hostarch.PageSize 139 } 140 if end := virtual + length; end > ring0.MaximumUserAddress { 141 length -= (end - ring0.MaximumUserAddress) 142 } 143 if length == 0 { 144 return 145 } 146 // Round physical up to the same alignment as the virtual 147 // address (with respect to faultBlockSize). 148 if offset := virtual &^ faultBlockMask; physical&^faultBlockMask != offset { 149 if newPhysical := (physical & faultBlockMask) + offset; newPhysical > physical { 150 physical = newPhysical // Round up by only a little bit. 151 } else { 152 physical = ((physical + faultBlockSize) & faultBlockMask) + offset 153 } 154 } 155 physicalRegions = append(physicalRegions, physicalRegion{ 156 region: region{ 157 virtual: virtual, 158 length: length, 159 }, 160 physical: physical, 161 }) 162 physical += length 163 } 164 lastExcludedEnd := uintptr(0) 165 for _, r := range excludedRegions { 166 addValidRegion(lastExcludedEnd, r.virtual-lastExcludedEnd) 167 lastExcludedEnd = r.virtual + r.length 168 } 169 addValidRegion(lastExcludedEnd, ring0.MaximumUserAddress-lastExcludedEnd) 170 171 // Dump our all physical regions. 172 for _, r := range physicalRegions { 173 log.Infof("physicalRegion: virtual [%x,%x) => physical [%x,%x)", 174 r.virtual, r.virtual+r.length, r.physical, r.physical+r.length) 175 } 176 return physicalRegions 177 } 178 179 // physicalInit initializes physical address mappings. 180 func physicalInit() { 181 physicalRegions = computePhysicalRegions(fillAddressSpace()) 182 } 183 184 // applyPhysicalRegions applies the given function on physical regions. 185 // 186 // Iteration continues as long as true is returned. The return value is the 187 // return from the last call to fn, or true if there are no entries. 188 // 189 // Precondition: physicalInit must have been called. 190 func applyPhysicalRegions(fn func(pr physicalRegion) bool) bool { 191 for _, pr := range physicalRegions { 192 if !fn(pr) { 193 return false 194 } 195 } 196 return true 197 } 198 199 // translateToPhysical translates the given virtual address. 200 // 201 // Precondition: physicalInit must have been called. 202 // 203 //go:nosplit 204 func translateToPhysical(virtual uintptr) (physical uintptr, length uintptr, ok bool) { 205 for _, pr := range physicalRegions { 206 if pr.virtual <= virtual && virtual < pr.virtual+pr.length { 207 physical = pr.physical + (virtual - pr.virtual) 208 length = pr.length - (virtual - pr.virtual) 209 ok = true 210 return 211 } 212 } 213 return 214 }