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