github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/kvm.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package builtin 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "regexp" 26 "strings" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/interfaces/kmod" 30 "github.com/snapcore/snapd/logger" 31 "github.com/snapcore/snapd/strutil" 32 ) 33 34 const kvmSummary = `allows access to the kvm device` 35 36 const kvmBaseDeclarationSlots = ` 37 kvm: 38 allow-installation: 39 slot-snap-type: 40 - core 41 deny-auto-connection: true 42 ` 43 44 const kvmConnectedPlugAppArmor = ` 45 # Description: Allow write access to kvm. 46 # See 'man kvm' for details. 47 48 /dev/kvm rw, 49 ` 50 51 var kvmConnectedPlugUDev = []string{`KERNEL=="kvm"`} 52 53 type kvmInterface struct { 54 commonInterface 55 } 56 57 var procCpuinfo = "/proc/cpuinfo" 58 var flagsMatcher = regexp.MustCompile(`(?m)^flags\s+:\s+(.*)$`).FindSubmatch 59 60 func getCpuFlags() (flags []string, err error) { 61 buf, err := ioutil.ReadFile(procCpuinfo) 62 if err != nil { 63 // if we can't read cpuinfo, we want to know _why_ 64 return nil, fmt.Errorf("unable to read %v: %v", procCpuinfo, err) 65 } 66 67 // want to capture the text after 'flags:' entry 68 match := flagsMatcher(buf) 69 if len(match) == 0 { 70 return nil, fmt.Errorf("%v does not contain a 'flags:' entry", procCpuinfo) 71 } 72 73 // match[0] has whole matching line, match[1] must exist as it has the captured text after 'flags:' 74 cpu_flags := strings.Fields(string(match[1])) 75 return cpu_flags, nil 76 } 77 78 func (iface *kvmInterface) KModConnectedPlug(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 79 // Check CPU capabilities to load suitable module 80 // NOTE: this only considers i386, x86_64 and amd64 CPUs, but some ARM, PPC and S390 CPUs also support KVM 81 m := "kvm" 82 cpu_flags, err := getCpuFlags() 83 if err != nil { 84 logger.Debugf("kvm: fetching cpu info failed: %v", err) 85 } 86 87 if strutil.ListContains(cpu_flags, "vmx") { 88 m = "kvm_intel" 89 } else if strutil.ListContains(cpu_flags, "svm") { 90 m = "kvm_amd" 91 } else { 92 // CPU appears not to support KVM extensions, fall back to bare kvm module as it appears 93 // sufficient for some architectures 94 logger.Noticef("kvm: failed to detect CPU specific KVM support, will attempt to modprobe generic KVM support") 95 } 96 97 if err := spec.AddModule(m); err != nil { 98 return nil 99 } 100 return nil 101 } 102 103 func init() { 104 registerIface(&kvmInterface{commonInterface{ 105 name: "kvm", 106 summary: kvmSummary, 107 implicitOnCore: true, 108 implicitOnClassic: true, 109 baseDeclarationSlots: kvmBaseDeclarationSlots, 110 connectedPlugAppArmor: kvmConnectedPlugAppArmor, 111 connectedPlugUDev: kvmConnectedPlugUDev, 112 reservedForOS: true, 113 }}) 114 }