]> rtime.felk.cvut.cz Git - jailhouse.git/blob - tools/jailhouse-hardware-check
Merge remote-tracking branch 'kiszka/master'
[jailhouse.git] / tools / jailhouse-hardware-check
1 #!/usr/bin/env python
2
3 # Jailhouse, a Linux-based partitioning hypervisor
4 #
5 # Copyright (c) Siemens AG, 2016
6 #
7 # Authors:
8 #  Jan Kiszka <jan.kiszka@siemens.com>
9 #
10 # This work is licensed under the terms of the GNU GPL, version 2.  See
11 # the COPYING file in the top-level directory.
12
13 from __future__ import print_function
14 import collections
15 import mmap
16 import os
17 import struct
18 import sys
19
20 check_passed = True
21
22
23 def check_feature(msg, ok, optional=False):
24     if not (ok or optional):
25         global check_passed
26         check_passed = False
27     print('%-32s%s' % (msg, 'ok' if ok else
28                             ('missing (optional)' if optional else 'MISSING')))
29     return ok
30
31
32 def parse_cpuinfo():
33     vendor = None
34     features = None
35     cpus = 0
36     with open('/proc/cpuinfo', 'r') as info:
37         for line in info:
38             if not line.strip():
39                 continue
40             key, value = line.split(':')
41             if key.strip() == 'vendor_id':
42                 if not vendor:
43                     vendor = value.strip()
44                 elif vendor != value.strip():
45                     print('ERROR: Inconsistent vendor string on CPU %d' % cpus,
46                           file=sys.stderr)
47                     sys.exit(2)
48                 cpus += 1
49             if key.strip() == 'flags':
50                 if not features:
51                     features = value.strip().split(' ')
52                 elif features != value.strip().split(' '):
53                     print('ERROR: Inconsistent feature set on CPU %d' % cpus,
54                           file=sys.stderr)
55                     sys.exit(2)
56     return (vendor, features, cpus)
57
58
59 class MSR:
60     IA32_FEATURE_CONTROL = 0x0000003a
61     IA32_VMX_BASIC = 0x00000480
62     IA32_VMX_PINBASED_CTLS = 0x00000481
63     IA32_VMX_PROCBASED_CTLS = 0x00000482
64     IA32_VMX_EXIT_CTLS = 0x00000483
65     IA32_VMX_ENTRY_CTLS = 0x00000484
66     IA32_VMX_MISC = 0x00000485
67     IA32_VMX_PROCBASED_CTLS2 = 0x0000048b
68     IA32_VMX_EPT_VPID_CAP = 0x0000048c
69     IA32_VMX_TRUE_PROCBASED_CTLS = 0x0000048e
70
71     def __init__(self, num_cpus):
72         self.num_cpus = num_cpus
73         self.msr = []
74         for n in range(self.num_cpus):
75             self.msr.append(open('/dev/cpu/%d/msr' % n, 'rb', 0))
76
77     def read(self, index):
78         try:
79             self.msr[0].seek(index)
80             value = struct.unpack('Q', self.msr[0].read(8))[0]
81         except:
82             return 0
83
84         for n in range(1, self.num_cpus):
85             self.msr[n].seek(index)
86             if value != struct.unpack('Q', self.msr[n].read(8))[0]:
87                 print('ERROR: Inconsistent value of MSR 0x%x on CPU %d' %
88                       (index, n), file=sys.stderr)
89                 sys.exit(2)
90             return value
91
92
93 class MMIO:
94     def __init__(self, base, size):
95         f = os.open('/dev/mem', os.O_RDONLY | os.O_SYNC)
96         self.mmap = mmap.mmap(f, size, mmap.MAP_SHARED, mmap.PROT_READ,
97                               offset=base)
98
99     def read64(self, offset):
100         self.mmap.seek(offset)
101         return struct.unpack('Q', self.mmap.read(8))[0]
102
103
104 class Sysconfig:
105     SIGNATURE_SIZE = 8
106     HVMEM_SIZE = 32
107     DBGCON_SIZE = 32
108     X86_MMCFGBASE_SIZE = 8
109     X86_MMCFGENDBUS_SIZE = 1
110     X86_PADDING = 5
111     X86_PMTMR_SIZE = 2
112     X86_MAX_IOMMU_UNITS = 8
113     X86_IOMMU_SIZE = 20
114
115     def __init__(self, path):
116         self.config = open(path, 'rb')
117         if self.config.read(Sysconfig.SIGNATURE_SIZE).decode() != 'JAILSYST':
118             print('Not a system configuration', file=sys.stderr)
119             sys.exit(1)
120
121     def parse_iommus(self):
122         self.config.seek(Sysconfig.SIGNATURE_SIZE + Sysconfig.HVMEM_SIZE +
123                          Sysconfig.DBGCON_SIZE + Sysconfig.X86_MMCFGBASE_SIZE +
124                          Sysconfig.X86_MMCFGENDBUS_SIZE +
125                          Sysconfig.X86_PADDING + Sysconfig.X86_PMTMR_SIZE)
126
127         keys = 'base size amd_bdf amd_base_cap amd_features'
128         IOMMU = collections.namedtuple('IOMMU', keys)
129
130         iommus = []
131         for n in range(Sysconfig.X86_MAX_IOMMU_UNITS):
132             data = self.config.read(Sysconfig.X86_IOMMU_SIZE)
133             iommu = IOMMU(*struct.unpack('QIHBxI', data))
134             iommus.append(iommu)
135         return iommus
136
137
138 def usage(exit_code):
139     prog = os.path.basename(sys.argv[0]).replace('-', ' ')
140     print('usage: %s SYSCONFIG' % prog)
141     sys.exit(exit_code)
142
143
144 if len(sys.argv) != 2:
145     usage(1)
146 if sys.argv[1] in ("--help", "-h"):
147     usage(0)
148
149 if os.uname()[4] not in ('x86_64', 'i686'):
150     print('Unsupported architecture', file=sys.stderr)
151     sys.exit(1)
152
153 config = Sysconfig(sys.argv[1])
154 iommu = config.parse_iommus()
155
156 (cpu_vendor, cpu_features, cpu_count) = parse_cpuinfo()
157
158 if not os.access('/dev/cpu/0/msr', os.R_OK):
159     if os.system('/sbin/modprobe msr'):
160         sys.exit(1)
161
162 msr = MSR(cpu_count)
163
164 print('Feature                         Availability')
165 print('------------------------------  ------------------')
166 check_feature('Number of CPUs > 1', cpu_count > 1)
167 check_feature('Long mode', 'lm' in cpu_features)
168
169 if cpu_vendor == 'GenuineIntel':
170     check_feature('x2APIC', 'x2apic' in cpu_features, True)
171     print()
172     check_feature('VT-x (VMX)', 'vmx' in cpu_features)
173
174     feature = msr.read(MSR.IA32_FEATURE_CONTROL)
175     check_feature('  VMX without TXT',
176                   (feature & (1 << 0)) == 0 or feature & (1 << 2))
177     check_feature('  IA32_TRUE_*_CLTS',
178                   msr.read(MSR.IA32_VMX_BASIC) & (1 << 55))
179
180     pinbased = msr.read(MSR.IA32_VMX_PINBASED_CTLS) >> 32
181     check_feature('  NMI exiting', pinbased & (1 << 3))
182     check_feature('  Preemption timer', pinbased & (1 << 6))
183
184     procbased = msr.read(MSR.IA32_VMX_PROCBASED_CTLS) >> 32
185     check_feature('  I/O bitmap', procbased & (1 << 25))
186     check_feature('  MSR bitmap', procbased & (1 << 28))
187     check_feature('  Secondary controls', procbased & (1 << 31))
188     check_feature('  Optional CR3 interception',
189                   (msr.read(MSR.IA32_VMX_TRUE_PROCBASED_CTLS) &
190                    (3 << 15)) == 0)
191
192     procbased2 = msr.read(MSR.IA32_VMX_PROCBASED_CTLS2) >> 32
193     check_feature('  Virtualize APIC access', procbased2 & (1 << 0))
194     check_feature('  RDTSCP', procbased2 & (1 << 3),
195                   'rdtscp' not in cpu_features)
196     check_feature('  Unrestricted guest', procbased2 & (1 << 7))
197
198     check_feature('  EPT', procbased2 & (1 << 1))
199     ept_cap = msr.read(MSR.IA32_VMX_EPT_VPID_CAP)
200     check_feature('    4-level page walk', ept_cap & (1 << 6))
201     check_feature('    EPTP write-back', ept_cap & (1 << 14))
202     check_feature('    2M pages', ept_cap & (1 << 16), True)
203     check_feature('    1G pages', ept_cap & (1 << 17), True)
204     check_feature('    INVEPT', ept_cap & (1 << 20))
205     check_feature('      Single or all-context', ept_cap & (3 << 25))
206
207     vmexit = msr.read(MSR.IA32_VMX_EXIT_CTLS) >> 32
208     check_feature('  VM-exit save IA32_PAT', vmexit & (1 << 18))
209     check_feature('  VM-exit load IA32_PAT', vmexit & (1 << 19))
210     check_feature('  VM-exit save IA32_EFER', vmexit & (1 << 20))
211     check_feature('  VM-exit load IA32_EFER', vmexit & (1 << 21))
212
213     vmentry = msr.read(MSR.IA32_VMX_ENTRY_CTLS) >> 32
214     check_feature('  VM-entry load IA32_PAT', vmentry & (1 << 14))
215     check_feature('  VM-entry load IA32_EFER', vmentry & (1 << 15))
216     check_feature('  Activity state HLT',
217                   msr.read(MSR.IA32_VMX_MISC) & (1 << 6))
218
219     for n in range(8):
220         if iommu[n].base == 0 and n > 0:
221             break
222         print()
223         check_feature('VT-d (IOMMU #%d)' % n, iommu[n].base)
224         if iommu[n].base == 0:
225             break
226         mmio = MMIO(iommu[n].base, iommu[n].size)
227         cap = mmio.read64(0x08)
228         check_feature('  Caching mode = 0', (cap & (1 << 7)) == 0)
229         check_feature('  39-bit AGAW', cap & (1 << 9), cap & (1 << 10))
230         check_feature('  48-bit AGAW', cap & (1 << 10), cap & (1 << 9))
231         check_feature('  2M pages', cap & (1 << 34), True)
232         check_feature('  1G pages', cap & (1 << 35), True)
233         ecap = mmio.read64(0x10)
234         check_feature('  Queued invalidation', ecap & (1 << 1))
235         check_feature('  Interrupt remapping', ecap & (1 << 3))
236         check_feature('  Extended interrupt mode', ecap & (1 << 4),
237                       'x2apic' not in cpu_features)
238
239 elif cpu_vendor == 'AuthenticAMD':
240     print()
241     check_feature('AMD-V (SVM)', 'svm' in cpu_features)
242     check_feature('  NPT', 'npt' in cpu_features)
243     check_feature('  Decode assist', 'decodeassists' in cpu_features, True)
244     check_feature('  AVIC', 'avic' in cpu_features, True)
245     check_feature('  Flush by ASID', 'flushbyasid' in cpu_features, True)
246
247     for n in range(8):
248         if iommu[n].base == 0 and n > 0:
249             break
250         print()
251         check_feature('AMD-Vi (IOMMU #%d)' % n, iommu[n].base)
252         if iommu[n].base == 0:
253             break
254
255         bdf = iommu[n].amd_bdf
256         path = '/sys/bus/pci/devices/0000:%02x:%02x.%x/config' % \
257             (bdf >> 8, (bdf >> 3) & 0x1f, bdf & 0x7)
258         with open(path, 'rb') as config:
259             config.seek(iommu[n].amd_base_cap)
260             (caps, base) = struct.unpack('QQ', config.read(16))
261
262         check_feature('  Extended feature register', caps & (1 << 27))
263         check_feature('  Valid base register',
264                       (base & (1 << 0)) == 0 or
265                       base == (iommu[n].base | (1 << 0)))
266
267         mmio = MMIO(iommu[n].base, iommu[n].size)
268         efr = mmio.read64(0x30)
269         if check_feature('  SMI filter', ((efr >> 16) & 0x3) == 1):
270             smi_filter_ok = True
271             num_filter_regs = 1 << ((efr >> 18) & 7)
272             for n in range(num_filter_regs):
273                 smi_freg = mmio.read64(0x60 + (n << 3))
274                 # must not be locked AND set to match against specific device
275                 if smi_freg & (1 << 17) and smi_freg & (1 << 16):
276                     smi_filter_ok = False
277             check_feature('    Valid filter registers', smi_filter_ok)
278
279         he_feature = iommu[n].amd_features if iommu[n].amd_features != 0 \
280             else efr
281         check_feature('  Hardware events', he_feature & (1 << 8), True)
282
283 else:
284     print('Unsupported CPU', file=sys.stderr)
285
286 print('\nCheck %s!' % ('passed' if check_passed else 'FAILED'))
287 sys.exit(0 if check_passed else 2)