From 6ff8920b3ba69c3f39ddd662babe1b848ad3eef8 Mon Sep 17 00:00:00 2001 From: Ioannis Sfakianakis Date: Thu, 26 Oct 2023 14:38:13 +0300 Subject: [PATCH] Added vmcap script in the verification image - vmcap script identifies the virtualization capabilities of x64 hosts. It does that by reading a special file in /dev/cpu//msr, which is an interface to read and write model specific registers (MSRs). Signed-off-by: Ioannis Sfakianakis --- pkg/mkverification-raw-efi/Dockerfile | 6 +- pkg/mkverification-raw-efi/verify | 5 + pkg/mkverification-raw-efi/vmcap.go | 387 ++++++++++++++++++++++++++ 3 files changed, 396 insertions(+), 2 deletions(-) create mode 100755 pkg/mkverification-raw-efi/vmcap.go diff --git a/pkg/mkverification-raw-efi/Dockerfile b/pkg/mkverification-raw-efi/Dockerfile index e8668e18c0..c871971ea7 100644 --- a/pkg/mkverification-raw-efi/Dockerfile +++ b/pkg/mkverification-raw-efi/Dockerfile @@ -15,7 +15,7 @@ ENV BUILD_PKGS mkinitfs grep patch make coreutils musl-dev gcc g++ perl \ util-linux-dev flex linux-headers glib-dev libxrandr-dev zlib-dev \ libusb-dev kmod-dev curl eudev-dev libdrm-dev i2c-tools-dev hwinfo \ binutils-dev libaio-dev zlib-static libvncserver-dev python3 pixman-dev \ - py3-setuptools bash perl cmake m4 pkgconf autoconf-archive tpm2-tss-dev + py3-setuptools bash perl cmake m4 pkgconf autoconf-archive tpm2-tss-dev go ENV PKGS mtools dosfstools libarchive-tools sgdisk e2fsprogs util-linux \ squashfs-tools coreutils tar dmidecode smartmontools libaio libaio-dev \ perl glib zlib libusb curl xz pciutils usbutils hdparm util-linux \ @@ -38,7 +38,9 @@ ADD https://github.com/linuxhw/build-stuff/releases/download/1.6/hw-probe-1.6-AI #ADD https://cloud-images.ubuntu.com/minimal/releases/jammy/release/ubuntu-22.04-minimal-cloudimg-amd64.img /out/ubuntu-22.04-minimal-cloudimg-amd64.img ADD https://github.com/tpm2-software/tpm2-tools/archive/5.2.tar.gz /out/5.2.tar.gz -COPY make-raw verify grub.cfg.in UsbInvocationScript.txt ./ +COPY make-raw verify grub.cfg.in UsbInvocationScript.txt vmcap.go ./ + +RUN go build vmcap.go && rm vmcap.go RUN tar -xf hw-probe-1.6-AI.tar.gz \ && tar -xf ddcutil-1.2.2.tar.gz \ diff --git a/pkg/mkverification-raw-efi/verify b/pkg/mkverification-raw-efi/verify index f3e51f37ef..4ab073f745 100755 --- a/pkg/mkverification-raw-efi/verify +++ b/pkg/mkverification-raw-efi/verify @@ -411,6 +411,11 @@ else echo "No watchdogs available" > "$REPORT/watchdogs.log" fi +ARCH=$(uname -m) +if [ "$ARCH" = "x86_64" ]; then + /vmcap > "$REPORT/vmcap.log" +fi + cat "$REPORT/summary.log" if [ -n "$REPORT" ]; then diff --git a/pkg/mkverification-raw-efi/vmcap.go b/pkg/mkverification-raw-efi/vmcap.go new file mode 100755 index 0000000000..b2a265bd0f --- /dev/null +++ b/pkg/mkverification-raw-efi/vmcap.go @@ -0,0 +1,387 @@ +// Copyright (c) 2023 Zededa, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "encoding/binary" + "fmt" + "os" + "strconv" + "strings" +) + +// For more information chekout out https://man7.org/linux/man-pages/man4/msr.4.html +// and https://cdrdv2.intel.com/v1/dl/getContent/671200 (Appending A -VMX capability +// reporting facility, on page 4483) + +const ( + basic = 0x480 + pinBasedCtls = 0x481 + procBasedCtls = 0x482 + exitCtls = 0x483 + entryCtls = 0x484 + miscCtls = 0x485 + procBasedCtls2 = 0x48B + eptVPIDCap = 0x48C + truePinBasedCtls = 0x48D + trueProcBasedCtls = 0x48E + trueExitCtls = 0x48F + trueEntryCtls = 0x490 + vmFunc = 0x491 + procBasedCtls3 = 0x492 +) + +type msr struct { + f *os.File +} + +func newMsr() (*msr, error) { + f, err := os.OpenFile("/dev/cpu/0/msr", os.O_RDONLY, 0) + if err != nil { + f, err = os.OpenFile("/dev/msr0", os.O_RDONLY, 0) + if err != nil { + return nil, err + } + } + return &msr{f: f}, nil +} + +func (m *msr) read(index uint64, defaultVal uint64) (uint64, error) { + p := make([]byte, 8) + _, err := m.f.Seek(int64(index), 0) + if err != nil { + return defaultVal, err + } + _, err = m.f.Read(p) + if err != nil { + return defaultVal, err + } + + data := binary.LittleEndian.Uint64(p) + + return data, nil +} + +type control struct { + name string + bits map[int]string + msr uint64 + trueMsr uint64 +} + +func (c *control) read(nr uint64) (uint32, uint32, error) { + m, err := newMsr() + if err != nil { + return 0, 0, err + } + val, _ := m.read(nr, 0) + return uint32(val & 0xffffffff), uint32(val >> 32), err +} + +func (c *control) show() { + fmt.Println(c.name) + mb1, cb1, err := c.read(c.msr) + tmb1, tcb1 := uint32(0), uint32(0) + if c.trueMsr != 0 { + tmb1, tcb1, err = c.read(c.trueMsr) + } + if err != nil { + fmt.Printf("Architecture not supported") + return + } + for bit := range c.bits { + zero := !(mb1&(1<> low) & ((1 << (high - low + 1)) - 1) + var out string + if v == 0 { + out = "no" + } else if v == 1 { + out = "yes" + } else { + out = strconv.FormatUint(v, 10) + } + fmt.Printf(" %-40s %s\n", name, out) + } +} + +func main() { + controls := []interface{}{ + misc{ + name: "Basic VMX Information", + bits: map[string]string{ + "0,30": "VMCS revision identifier", + "32,44": "Size of VMCS regions", + "48": "Physical address width of VMCS regions", + "49": "Dual-monitor support", + "50,53": "VMCS memory type", + "54": "VM-exit information due to INS/OUTS", + "55": "VMX capability for IA32_VMX_TRUE_*Ctls", + "56": "VM entry for hardware exception", + }, + msr: basic, + }, + control{ + name: "Pin-based controls", + bits: map[int]string{ + 0: "External-interrupt exiting", + 3: "NMI exiting", + 5: "Virtual NMIs", + 6: "Activate VMX-preemption timer", + 7: "Process posted interrupts", + }, + msr: pinBasedCtls, + trueMsr: truePinBasedCtls, + }, + control{ + name: "Primary processor-based controls", + bits: map[int]string{ + 2: "Interrupt-window exiting", + 3: "Use TSC offsetting", + 7: "HLT exiting", + 9: "INVLPG exiting", + 10: "MWAIT exiting", + 11: "RDPMC exiting", + 12: "RDTSC exiting", + 15: "CR3-load exiting", + 16: "CR3-store exiting", + 17: "Activate tertiary controls", + 19: "CR8-load exiting", + 20: "CR8-store exiting", + 21: "Use TPR shadow", + 22: "NMI-window exiting", + 23: "MOV-DR exiting", + 24: "Unconditional I/O exiting", + 25: "Use I/O bitmaps", + 27: "Monitor trap flag", + 28: "Use MSR bitmaps", + 29: "MONITOR exiting", + 30: "PAUSE exiting", + 31: "Activate secondary controls", + }, + msr: procBasedCtls, + trueMsr: trueProcBasedCtls, + }, + control{ + name: "Secondary processor-based controls", + bits: map[int]string{ + 0: "Virtualize APIC accesses", + 1: "Enable EPT", + 2: "Descriptor-table exiting", + 3: "Enable RDTSCP", + 4: "Virtualize x2APIC mode", + 5: "Enable VPID", + 6: "WBINVD exiting", + 7: "Unrestricted guest", + 8: "APIC-register virtualization", + 9: "Virtual-interrupt delivery", + 10: "PAUSE-loop exiting", + 11: "RDRAND exiting", + 12: "Enable INVPCID", + 13: "Enable VM functions", + 14: "VMCS shadowing", + 15: "Enable ENCLS exiting", + 16: "RDSEED exiting", + 17: "Enable PML", + 18: "EPT-violation #VE", + 19: "Conceal VMX from PT", + 20: "Enable XSAVES/XRSTORS", + 21: "PASID translation", + 22: "Mode-based execute control for EPT", + 23: "Sub-page write permissions for EPT", + 24: "Intel PT uses guest physical addresses", + 25: "Use TSC scaling", + 26: "Enable user wait and pause", + 27: "Enable PCONFIG", + 28: "Enable ENCLV exiting", + 30: "VMM bus-lock detection", + 31: "Instruction timeout", + }, + msr: procBasedCtls2, + }, + + allowedControl{ + control: &control{ + name: "Tertiary processor-based controls", + bits: map[int]string{ + 4: "IPI virtualization", + 7: "Virtualize IA32_SPEC_CTRL", + }, + msr: procBasedCtls3, + }, + }, + + control{ + name: "Primary VM-Exit controls", + bits: map[int]string{ + 2: "Save debug controls", + 9: "Host address-space size", + 12: "Load IA32_PERF_GLOBAL_CTRL", + 15: "Acknowledge interrupt on exit", + 18: "Save IA32_PAT", + 19: "Load IA32_PAT", + 20: "Save IA32_EFER", + 21: "Load IA32_EFER", + 22: "Save VMX-preemption timer value", + 23: "Clear IA32_BNDCFGS", + 24: "Conceal VMX from PT", + 25: "Clear IA32_RTIT_CTL", + 26: "Clear IA32_LBR_CTL", + 27: "Clear UINV", + 28: "Load CET state", + 29: "Load PKRS", + 30: "Save IA32_PERF_GLOBAL_CTL", + 31: "Activate secondary controls", + }, + msr: exitCtls, + trueMsr: trueExitCtls, + }, + + control{ + name: "VM-Entry controls", + bits: map[int]string{ + 2: "Load debug controls", + 9: "IA-32e mode guest", + 10: "Entry to SMM", + 11: "Deactivate dual-monitor treatment", + 13: "Load IA32_PERF_GLOBAL_CTRL", + 14: "Load IA32_PAT", + 15: "Load IA32_EFER", + 16: "Load IA32_BNDCFGS", + 17: "Conceal VMX from PT", + 18: "Load IA32_RTIT_CTL", + 19: "Load UINV", + 20: "Load CET state", + 21: "Load guest IA32_LBR_CTL", + 22: "Load PKRS", + }, + msr: entryCtls, + trueMsr: trueEntryCtls, + }, + + misc{ + name: "Miscellaneous data", + bits: map[string]string{ + "0,4": "VMX-preemption timer in relation to TSC", + "5": "Store EFER.LMA into IA-32e", + "6": "HLT activity state", + "7": "Shutdown activity state", + "8": "Wait-for-SIPI activity state", + "14": "Intel PT in VMX operation", + "15": "RDMSR in system-management mode (SMM)", + "16,24": "Number of CR3-target values", + "25,27": "Recommended max MSR-load/store number", + "28": "IA32_SMM_MONITOR_CTL[2] can be set to 1", + "29": "VMWRITE for writing VM-exit information", + "30": "Inject event with insn length=0", + "32,63": "MSEG revision identifier", + }, + msr: miscCtls, + }, + + misc{ + name: "VPID and EPT capabilities", + bits: map[string]string{ + "0": "Execute-only translations by EPT", + "6": "Page-walk length of 4", + "7": "Page-walk length of 5", + "8": "EPT paging-structure memory type UC", + "14": "EPT paging-structure memory type WB", + "16": "EPT PDE to map a 2MB page", + "17": "EPT PDPTE to map a 1GB page", + "20": "INVEPT supported", + "21": "Accessed and dirty flags for EPT", + "22": "Advanced VM-exit info for EPT violations", + "25": "Single-context INVEPT type supported", + "26": "All-context INVEPT type supported", + "32": "INVVPID supported", + "40": "Individual-address INVVPID", + "41": "Single-context INVVPID type supported", + "42": "All-context INVVPID type supported", + "43": "Single-context-retaining-globals INVVPID", + "48,53": "Maximum HLAT prefix size", + }, + msr: eptVPIDCap, + }, + misc{ + name: "VM Functions", + bits: map[string]string{ + "0": "EPTP Switching", + }, + msr: vmFunc, + }, + } + + for _, c := range controls { + cc, ok := c.(control) + if ok { + cc.show() + } + cm, ok := c.(misc) + if ok { + cm.show() + } + ca, ok := c.(allowedControl) + if ok { + ca.show() + } + fmt.Println() + } +}