diff options
Diffstat (limited to 'xen_maskcalc.py')
| -rw-r--r-- | xen_maskcalc.py | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/xen_maskcalc.py b/xen_maskcalc.py new file mode 100644 index 0000000..0d12227 --- /dev/null +++ b/xen_maskcalc.py @@ -0,0 +1,395 @@ +#!/usr/bin/python3 + +# Xen Mask Calculator - Calculate CPU masking information based on cpuid(1) +# Copyright (C) 2017 Armando Vega +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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/>. + +import argparse +import sys +import os + + +EAX1_MATCH = '0x00000001 0x00:' +EAX7_MATCH = '0x00000007 0x00:' +EXP_LINELN = 76 + +libxl_names_ecx1 = [] +libxl_names_edx1 = [] +libvirt_names_ecx1 = [] +libvirt_names_edx1 = [] + +libxl_names_ebx7 = [] +libxl_names_ecx7 = [] +libvirt_names_ebx7 = [] +libvirt_names_ecx7 = [] + +def fill_ecx1(bit, libxl, libvirt): + if libxl_names_ecx1[bit]: + print("ecx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ecx1[bit], libvirt_names_ecx1[bit], libxl, libvirt)) + return + libxl_names_ecx1[bit] = libxl + libvirt_names_ecx1[bit] = libvirt + +def fill_edx1(bit, libxl, libvirt): + if libxl_names_edx1[bit]: + print("edx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_edx1[bit], libvirt_names_edx1[bit], libxl, libvirt)) + return + libxl_names_edx1[bit] = libxl + libvirt_names_edx1[bit] = libvirt + +def fill_ebx7(bit, libxl, libvirt): + if libxl_names_ebx7[bit]: + print("edx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ebx7[bit], libvirt_names_ebx7[bit], libxl, libvirt)) + return + libxl_names_ebx7[bit] = libxl + libvirt_names_ebx7[bit] = libvirt + +def fill_ecx7(bit, libxl, libvirt): + if libxl_names_ecx7[bit]: + print("ecx bit %s already set: libxl %s libvirt %s. Ignoring %s/%s\n" % (bit, libxl_names_ecx7[bit], libvirt_names_ecx7[bit], libxl, libvirt)) + return + libxl_names_ecx7[bit] = libxl + libvirt_names_ecx7[bit] = libvirt + +def fill_bit_names(): + for i in range(0,32): + libxl_names_ecx1.append(None) + libxl_names_edx1.append(None) + libxl_names_ebx7.append(None) + libxl_names_ecx7.append(None) + libvirt_names_ecx1.append(None) + libvirt_names_edx1.append(None) + libvirt_names_ebx7.append(None) + libvirt_names_ecx7.append(None) + + fill_ecx1(0, "sse3", "pni") + fill_ecx1(1, "pclmulqdq", "pclmuldq") + fill_ecx1(2, "dtes64", "dtes64") + fill_ecx1(3, "monitor", "monitor") + fill_ecx1(4, "dscpl", "ds_cpl") + fill_ecx1(5, "vmx", "vmx") + fill_ecx1(6, "smx", "smx") + fill_ecx1(7, "est", "est") + fill_ecx1(8, "tm2", "tm2") + fill_ecx1(9, "ssse3", "ssse3") + fill_ecx1(10, "cntxid", "cid") + fill_ecx1(12, "fma", "fma") + fill_ecx1(13, "cmpxchg16", "cx16") + fill_ecx1(14, "xtpr", "xtpr") + fill_ecx1(15, "pdcm", "pdcm") + fill_ecx1(17, "pcid", "pcid") + fill_ecx1(18, "dca", "dca") + fill_ecx1(19, "sse4_1", "sse4.1") + fill_ecx1(20, "sse4_2", "sse4.2") + fill_ecx1(21, "x2apic", "x2apic") + fill_ecx1(22, "movbe", "movbe") + fill_ecx1(23, "popcnt", "popcnt") + fill_ecx1(24, "tsc-deadline", "tsc-deadline") + fill_ecx1(25, "aes", "aes") + fill_ecx1(26, "xsave", "xsave") + fill_ecx1(27, "osxsave", "osxsave") + fill_ecx1(28, "avx", "avx") + fill_ecx1(29, "f16c", "f16c") + fill_ecx1(30, "rdrand", "rdrand") + fill_ecx1(31, "hypervisor", "hypervisor") + + fill_edx1(0, "fpu", "fpu") + fill_edx1(1, "vme", "vme") + fill_edx1(2, "de", "de") + fill_edx1(3, "pse", "pse") + fill_edx1(4, "tsc", "tsc") + fill_edx1(5, "msr", "msr") + fill_edx1(6, "pae", "pae") + fill_edx1(7, "mce", "mce") + fill_edx1(8, "cmpxchg8", "cx8") + fill_edx1(9, "apic", "apic") + fill_edx1(11, "sysenter", "sep") + fill_edx1(12, "mtrr", "mtrr") + fill_edx1(13, "pge", "pge") + fill_edx1(14, "mca", "mca") + fill_edx1(15, "cmov", "cmov") + fill_edx1(16, "pat", "pat") + fill_edx1(17, "pse36", "pse36") + fill_edx1(18, "psn", "pn") + fill_edx1(19, "clfsh", "clflush") + fill_edx1(21, "ds", "ds") + fill_edx1(22, "acpi", "acpi") + fill_edx1(23, "mmx", "mmx") + fill_edx1(24, "fxsr", "fxsr") + fill_edx1(25, "sse", "sse") + fill_edx1(26, "sse2", "sse2") + fill_edx1(27, "ss", "ss") + fill_edx1(28, "htt", "ht") + fill_edx1(29, "tm", "tm") + fill_edx1(30, "ia64", "ia64") + fill_edx1(31, "pbe", "pbe") + + fill_ebx7(0, "fsgsbase", "fsgsbase") + fill_ebx7(1, "tsc_adjust", "tsc_adjust") + fill_ebx7(3, "bmi1", "bmi1") + fill_ebx7(4, "hle", "hle") + fill_ebx7(5, "avx2", "avx2") + fill_ebx7(7, "smep", "smep") + fill_ebx7(8, "bmi2", "bmi2") + fill_ebx7(9, "erms", "erms") + fill_ebx7(10, "invpcid", "invpcid") + fill_ebx7(11, "rtm", "rtm") + fill_ebx7(12, "cmt", "cmt") + fill_ebx7(14, "mpx", "mpx") + fill_ebx7(16, "avx512f", "avx512f") + fill_ebx7(17, "avx512dq", "avx512dq") + fill_ebx7(18, "rdseed", "rdseed") + fill_ebx7(19, "adx", "adx") + fill_ebx7(20, "smap", "smap") + fill_ebx7(21, "avx512-ifma", "avx512-ifma") + fill_ebx7(23, "clflushopt", "clflushopt") + fill_ebx7(24, "clwb", "clwb") + fill_ebx7(26, "avx512pf", "avx512pf") + fill_ebx7(27, "avx512er", "avx512er") + fill_ebx7(28, "avx512cd", "avx512cd") + fill_ebx7(29, "sha", "sha") + fill_ebx7(30, "avx512bw", "avx512bw") + fill_ebx7(31, "avx512vl", "avx512vl") + + fill_ecx7(0, "prefetchwt1", "prefetchwt1") + fill_ecx7(1, "avx512-vbmi", "avx512-vbmi") + fill_ecx7(2, "umip", "umip") + fill_ecx7(3, "pku", "pku") + fill_ecx7(4, "ospke", "ospke") + fill_ecx7(6, "avx512-vbmi2", "avx512-vbmi2") + fill_ecx7(8, "gfni", "gfni") + fill_ecx7(9, "vaes", "vaes") + fill_ecx7(10, "vpclmulqdq", "vpclmulqdq") + fill_ecx7(11, "avx512-vnni", "avx512-vnni") + fill_ecx7(12, "avx512-bitalg", "avx512-bitalg") + fill_ecx7(14, "avx512-vpopcntdq", "avx512-vpopcntdq") + fill_ecx7(22, "rdpid", "rdpid") + fill_ecx7(25, "cldemote", "cldemote") + + +def get_register_mask(regs): + """ Take a list of register values and return the calculated mask """ + reg_n = len(regs) + mask = '' + for idx in range(32): + counter = 0 + for reg in regs: + counter += 1 if (reg & (1 << idx) > 0) else 0 + # if we have all 1s or all 0s we don't mask the bit + if counter == reg_n or counter == 0: + mask = mask + 'x' + else: + mask = mask + '0' + # we calculated the mask in reverse, so we reverse it again + return mask[::-1] + + +def print_xl_masking_config(nodes): + """ Take a dictionary of nodes containing their registers and print out CPUID masking configuration for xl """ + nomasking = 'x' * 32 + libxl = [] + libvirt = [] + eax1_ecx_regs = [] + eax1_edx_regs = [] + eax7_ebx_regs = [] + eax7_ecx_regs = [] + for node in nodes: + eax1_ecx_regs.append(nodes[node]['eax1_ecx']) + eax1_edx_regs.append(nodes[node]['eax1_edx']) + eax7_ebx_regs.append(nodes[node]['eax7_ebx']) + eax7_ecx_regs.append(nodes[node]['eax7_ecx']) + # Get masks for the EAX1 and EAX7 registers + eax1_ecx_mask = get_register_mask(eax1_ecx_regs) + eax1_edx_mask = get_register_mask(eax1_edx_regs) + eax7_ebx_mask = get_register_mask(eax7_ebx_regs) + eax7_ecx_mask = get_register_mask(eax7_ecx_regs) + # Build the xl CPUID config + cpuid_config = 'cpuid = [\n "0x00000001:ecx=' + eax1_ecx_mask + if eax1_edx_mask != nomasking: + cpuid_config += ',edx=' + eax1_edx_mask + cpuid_config += '",\n' + cpuid_config += ' "0x00000007,0x00:ebx=' + eax7_ebx_mask + if eax7_ecx_mask != nomasking: + cpuid_config += ',ecx=' + eax7_ecx_mask + cpuid_config += '"\n' + cpuid_config += ']' + print(cpuid_config) + + bitnum = len(eax1_ecx_mask) + while bitnum > 0: + bitnum -= 1 + bitval = eax1_ecx_mask[len(eax1_ecx_mask) - 1 - bitnum] + if bitval == "0" and libxl_names_ecx1[bitnum]: + libxl.append(libxl_names_ecx1[bitnum] + "=0") + libvirt.append(libvirt_names_ecx1[bitnum]) + + bitnum = len(eax1_edx_mask) + while bitnum > 0: + bitnum -= 1 + bitval = eax1_edx_mask[len(eax1_edx_mask) - 1 - bitnum] + if bitval == "0" and libxl_names_edx1[bitnum]: + libxl.append(libxl_names_edx1[bitnum] + "=0") + libvirt.append(libvirt_names_edx1[bitnum]) + + bitnum = len(eax7_ebx_mask) + while bitnum > 0: + bitnum -= 1 + bitval = eax7_ebx_mask[len(eax7_ebx_mask) - 1 - bitnum] + if bitval == "0" and libxl_names_ebx7[bitnum]: + libxl.append(libxl_names_ebx7[bitnum] + "=0") + libvirt.append(libvirt_names_ebx7[bitnum]) + + bitnum = len(eax7_ecx_mask) + while bitnum > 0: + bitnum -= 1 + bitval = eax7_ecx_mask[len(eax7_ecx_mask) - 1 - bitnum] + if bitval == "0" and libxl_names_ecx7[bitnum]: + libxl.append(libxl_names_ecx7[bitnum] + "=0") + libvirt.append(libvirt_names_ecx7[bitnum]) + + if len(libxl) > 0: + output = "cpuid = [ host" + for i in libxl: + output += "," + i + output += " ]" + print(output) + + print("<domain>") + print(" <cpu>") + for i in libvirt: + print(" <feature policy='optional' name='%s' />" % i) + print(" </cpu>") + print("</domain>") + + +def print_verbose_masking_info(nodes): + """ Take a dictionary of nodes containing their registers and print out verbose mask derivation information """ + eax1_ecx_regs = [] + eax1_edx_regs = [] + eax7_ebx_regs = [] + eax7_ecx_regs = [] + for node in nodes: + eax1_ecx_regs.append(nodes[node]['eax1_ecx']) + eax1_edx_regs.append(nodes[node]['eax1_edx']) + eax7_ebx_regs.append(nodes[node]['eax7_ebx']) + eax7_ecx_regs.append(nodes[node]['eax7_ecx']) + + print("") + print('== Detailed mask derivation info ==') + print("") + + print('EAX1 ECX registers:') + for reg in eax1_ecx_regs: + print('{0:032b}'.format(reg)) + print('================================') + print(get_register_mask(eax1_ecx_regs)) + + print("") + print('EAX1 EDX registers:') + for reg in eax1_edx_regs: + print('{0:032b}'.format(reg)) + print('================================') + print(get_register_mask(eax1_edx_regs)) + + print("") + print('EAX7,0 EBX registers:') + for reg in eax7_ebx_regs: + print('{0:032b}'.format(reg)) + print('================================') + print(get_register_mask(eax7_ebx_regs)) + + print("") + print('EAX7,0 ECX registers:') + for reg in eax7_ecx_regs: + print('{0:032b}'.format(reg)) + print('================================') + print(get_register_mask(eax7_ecx_regs)) + + +if __name__ == '__main__': + epilog = """The individual 'node_files' are generated with 'cpuid -1r': + server1~$ cpuid -1r > node1 + server2~$ cpuid -1r > node2 + server3~$ cpuid -1r > node3 + + ~$ {0} node1 node2 node3 + + Use 'zypper install cpuid' to install the cpuid.rpm. + +Note: Run 'cpuid' with NATIVE boot instead of dom0 to get the complete cpid value. +Xen hides some bits from dom0! + """.format(sys.argv[0]) + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='A utility that calculates a XEN CPUID difference mask', + epilog=epilog + ) + parser.add_argument('node_files', nargs='*', help='Filenames of XEN node CPUID outputs') + parser.add_argument('-v', '--verbose', action='store_true', help='Get detailed mask derivation information') + args = parser.parse_args() + if len(args.node_files) < 2: + print('Need at least 2 files to do the comparison!') + parser.print_help() + sys.exit(1) + + fill_bit_names() + nodes = dict() + for node in args.node_files: + if os.path.isfile(node): + try: + f = open(node) + except IOError as e: + print("I/O error({0}): {1}".format(e.errno, e.strerror)) + sys.exit(1) + else: + lines = [line.strip() for line in f] + eax1 = '' + eax7 = '' + # try to match the lines containing interesting registers + # EAX1 - Processor Info and Feature Bits + # EAX7 - Extended features + for line in lines: + if line.startswith(EAX1_MATCH): + eax1 = line + elif line.startswith(EAX7_MATCH): + eax7 = line + # if we get garbled data we should probably just give up + if len(eax1) < EXP_LINELN or len(eax7) < EXP_LINELN: + print('ERROR: invalid data format in file : ' + node) + sys.exit(1) + + # check if we can actually parse the strings into integers + try: + eax1_ecx = int(eax1.split()[4].split('=')[1], 0) + eax1_edx = int(eax1.split()[5].split('=')[1], 0) + eax7_ebx = int(eax7.split()[3].split('=')[1], 0) + eax7_ecx = int(eax7.split()[4].split('=')[1], 0) + except ValueError: + print('ERROR: invalid data format in file: ' + node) + sys.exit(1) + + nodes[node] = dict() + nodes[node]['eax1_ecx'] = eax1_ecx + nodes[node]['eax1_edx'] = eax1_edx + nodes[node]['eax7_ebx'] = eax7_ebx + nodes[node]['eax7_ecx'] = eax7_ecx + f.close() + else: + print('File not found: ' + node) + sys.exit(1) + + print_xl_masking_config(nodes) + if args.verbose: + print_verbose_masking_info(nodes) |
