xref: /linux/tools/testing/selftests/x86/bugs/common.py (revision 6f5bf947bab06f37ff931c359fd5770c4d9cbf87)
1*7a9b709eSPawan Gupta#!/usr/bin/env python3
2*7a9b709eSPawan Gupta# SPDX-License-Identifier: GPL-2.0
3*7a9b709eSPawan Gupta#
4*7a9b709eSPawan Gupta# Copyright (c) 2025 Intel Corporation
5*7a9b709eSPawan Gupta#
6*7a9b709eSPawan Gupta# This contains kselftest framework adapted common functions for testing
7*7a9b709eSPawan Gupta# mitigation for x86 bugs.
8*7a9b709eSPawan Gupta
9*7a9b709eSPawan Guptaimport os, sys, re, shutil
10*7a9b709eSPawan Gupta
11*7a9b709eSPawan Guptasys.path.insert(0, '../../kselftest')
12*7a9b709eSPawan Guptaimport ksft
13*7a9b709eSPawan Gupta
14*7a9b709eSPawan Guptadef read_file(path):
15*7a9b709eSPawan Gupta    if not os.path.exists(path):
16*7a9b709eSPawan Gupta        return None
17*7a9b709eSPawan Gupta    with open(path, 'r') as file:
18*7a9b709eSPawan Gupta        return file.read().strip()
19*7a9b709eSPawan Gupta
20*7a9b709eSPawan Guptadef cpuinfo_has(arg):
21*7a9b709eSPawan Gupta    cpuinfo = read_file('/proc/cpuinfo')
22*7a9b709eSPawan Gupta    if arg in cpuinfo:
23*7a9b709eSPawan Gupta        return True
24*7a9b709eSPawan Gupta    return False
25*7a9b709eSPawan Gupta
26*7a9b709eSPawan Guptadef cmdline_has(arg):
27*7a9b709eSPawan Gupta    cmdline = read_file('/proc/cmdline')
28*7a9b709eSPawan Gupta    if arg in cmdline:
29*7a9b709eSPawan Gupta        return True
30*7a9b709eSPawan Gupta    return False
31*7a9b709eSPawan Gupta
32*7a9b709eSPawan Guptadef cmdline_has_either(args):
33*7a9b709eSPawan Gupta    cmdline = read_file('/proc/cmdline')
34*7a9b709eSPawan Gupta    for arg in args:
35*7a9b709eSPawan Gupta        if arg in cmdline:
36*7a9b709eSPawan Gupta            return True
37*7a9b709eSPawan Gupta    return False
38*7a9b709eSPawan Gupta
39*7a9b709eSPawan Guptadef cmdline_has_none(args):
40*7a9b709eSPawan Gupta    return not cmdline_has_either(args)
41*7a9b709eSPawan Gupta
42*7a9b709eSPawan Guptadef cmdline_has_all(args):
43*7a9b709eSPawan Gupta    cmdline = read_file('/proc/cmdline')
44*7a9b709eSPawan Gupta    for arg in args:
45*7a9b709eSPawan Gupta        if arg not in cmdline:
46*7a9b709eSPawan Gupta            return False
47*7a9b709eSPawan Gupta    return True
48*7a9b709eSPawan Gupta
49*7a9b709eSPawan Guptadef get_sysfs(bug):
50*7a9b709eSPawan Gupta    return read_file("/sys/devices/system/cpu/vulnerabilities/" + bug)
51*7a9b709eSPawan Gupta
52*7a9b709eSPawan Guptadef sysfs_has(bug, mitigation):
53*7a9b709eSPawan Gupta    status = get_sysfs(bug)
54*7a9b709eSPawan Gupta    if mitigation in status:
55*7a9b709eSPawan Gupta        return True
56*7a9b709eSPawan Gupta    return False
57*7a9b709eSPawan Gupta
58*7a9b709eSPawan Guptadef sysfs_has_either(bugs, mitigations):
59*7a9b709eSPawan Gupta    for bug in bugs:
60*7a9b709eSPawan Gupta        for mitigation in mitigations:
61*7a9b709eSPawan Gupta            if sysfs_has(bug, mitigation):
62*7a9b709eSPawan Gupta                return True
63*7a9b709eSPawan Gupta    return False
64*7a9b709eSPawan Gupta
65*7a9b709eSPawan Guptadef sysfs_has_none(bugs, mitigations):
66*7a9b709eSPawan Gupta    return not sysfs_has_either(bugs, mitigations)
67*7a9b709eSPawan Gupta
68*7a9b709eSPawan Guptadef sysfs_has_all(bugs, mitigations):
69*7a9b709eSPawan Gupta    for bug in bugs:
70*7a9b709eSPawan Gupta        for mitigation in mitigations:
71*7a9b709eSPawan Gupta            if not sysfs_has(bug, mitigation):
72*7a9b709eSPawan Gupta                return False
73*7a9b709eSPawan Gupta    return True
74*7a9b709eSPawan Gupta
75*7a9b709eSPawan Guptadef bug_check_pass(bug, found):
76*7a9b709eSPawan Gupta    ksft.print_msg(f"\nFound: {found}")
77*7a9b709eSPawan Gupta    # ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
78*7a9b709eSPawan Gupta    ksft.test_result_pass(f'{bug}: {found}')
79*7a9b709eSPawan Gupta
80*7a9b709eSPawan Guptadef bug_check_fail(bug, found, expected):
81*7a9b709eSPawan Gupta    ksft.print_msg(f'\nFound:\t {found}')
82*7a9b709eSPawan Gupta    ksft.print_msg(f'Expected:\t {expected}')
83*7a9b709eSPawan Gupta    ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
84*7a9b709eSPawan Gupta    ksft.test_result_fail(f'{bug}: {found}')
85*7a9b709eSPawan Gupta
86*7a9b709eSPawan Guptadef bug_status_unknown(bug, found):
87*7a9b709eSPawan Gupta    ksft.print_msg(f'\nUnknown status: {found}')
88*7a9b709eSPawan Gupta    ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
89*7a9b709eSPawan Gupta    ksft.test_result_fail(f'{bug}: {found}')
90*7a9b709eSPawan Gupta
91*7a9b709eSPawan Guptadef basic_checks_sufficient(bug, mitigation):
92*7a9b709eSPawan Gupta    if not mitigation:
93*7a9b709eSPawan Gupta        bug_status_unknown(bug, "None")
94*7a9b709eSPawan Gupta        return True
95*7a9b709eSPawan Gupta    elif mitigation == "Not affected":
96*7a9b709eSPawan Gupta        ksft.test_result_pass(bug)
97*7a9b709eSPawan Gupta        return True
98*7a9b709eSPawan Gupta    elif mitigation == "Vulnerable":
99*7a9b709eSPawan Gupta        if cmdline_has_either([f'{bug}=off', 'mitigations=off']):
100*7a9b709eSPawan Gupta            bug_check_pass(bug, mitigation)
101*7a9b709eSPawan Gupta            return True
102*7a9b709eSPawan Gupta    return False
103*7a9b709eSPawan Gupta
104*7a9b709eSPawan Guptadef get_section_info(vmlinux, section_name):
105*7a9b709eSPawan Gupta    from elftools.elf.elffile import ELFFile
106*7a9b709eSPawan Gupta    with open(vmlinux, 'rb') as f:
107*7a9b709eSPawan Gupta        elffile = ELFFile(f)
108*7a9b709eSPawan Gupta        section = elffile.get_section_by_name(section_name)
109*7a9b709eSPawan Gupta        if section is None:
110*7a9b709eSPawan Gupta            ksft.print_msg("Available sections in vmlinux:")
111*7a9b709eSPawan Gupta            for sec in elffile.iter_sections():
112*7a9b709eSPawan Gupta                ksft.print_msg(sec.name)
113*7a9b709eSPawan Gupta            raise ValueError(f"Section {section_name} not found in {vmlinux}")
114*7a9b709eSPawan Gupta        return section['sh_addr'], section['sh_offset'], section['sh_size']
115*7a9b709eSPawan Gupta
116*7a9b709eSPawan Guptadef get_patch_sites(vmlinux, offset, size):
117*7a9b709eSPawan Gupta    import struct
118*7a9b709eSPawan Gupta    output = []
119*7a9b709eSPawan Gupta    with open(vmlinux, 'rb') as f:
120*7a9b709eSPawan Gupta        f.seek(offset)
121*7a9b709eSPawan Gupta        i = 0
122*7a9b709eSPawan Gupta        while i < size:
123*7a9b709eSPawan Gupta            data = f.read(4)  # s32
124*7a9b709eSPawan Gupta            if not data:
125*7a9b709eSPawan Gupta                break
126*7a9b709eSPawan Gupta            sym_offset = struct.unpack('<i', data)[0] + i
127*7a9b709eSPawan Gupta            i += 4
128*7a9b709eSPawan Gupta            output.append(sym_offset)
129*7a9b709eSPawan Gupta    return output
130*7a9b709eSPawan Gupta
131*7a9b709eSPawan Guptadef get_instruction_from_vmlinux(elffile, section, virtual_address, target_address):
132*7a9b709eSPawan Gupta    from capstone import Cs, CS_ARCH_X86, CS_MODE_64
133*7a9b709eSPawan Gupta    section_start = section['sh_addr']
134*7a9b709eSPawan Gupta    section_end = section_start + section['sh_size']
135*7a9b709eSPawan Gupta
136*7a9b709eSPawan Gupta    if not (section_start <= target_address < section_end):
137*7a9b709eSPawan Gupta        return None
138*7a9b709eSPawan Gupta
139*7a9b709eSPawan Gupta    offset = target_address - section_start
140*7a9b709eSPawan Gupta    code = section.data()[offset:offset + 16]
141*7a9b709eSPawan Gupta
142*7a9b709eSPawan Gupta    cap = init_capstone()
143*7a9b709eSPawan Gupta    for instruction in cap.disasm(code, target_address):
144*7a9b709eSPawan Gupta        if instruction.address == target_address:
145*7a9b709eSPawan Gupta            return instruction
146*7a9b709eSPawan Gupta    return None
147*7a9b709eSPawan Gupta
148*7a9b709eSPawan Guptadef init_capstone():
149*7a9b709eSPawan Gupta    from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_OPT_SYNTAX_ATT
150*7a9b709eSPawan Gupta    cap = Cs(CS_ARCH_X86, CS_MODE_64)
151*7a9b709eSPawan Gupta    cap.syntax = CS_OPT_SYNTAX_ATT
152*7a9b709eSPawan Gupta    return cap
153*7a9b709eSPawan Gupta
154*7a9b709eSPawan Guptadef get_runtime_kernel():
155*7a9b709eSPawan Gupta    import drgn
156*7a9b709eSPawan Gupta    return drgn.program_from_kernel()
157*7a9b709eSPawan Gupta
158*7a9b709eSPawan Guptadef check_dependencies_or_skip(modules, script_name="unknown test"):
159*7a9b709eSPawan Gupta    for mod in modules:
160*7a9b709eSPawan Gupta        try:
161*7a9b709eSPawan Gupta            __import__(mod)
162*7a9b709eSPawan Gupta        except ImportError:
163*7a9b709eSPawan Gupta            ksft.test_result_skip(f"Skipping {script_name}: missing module '{mod}'")
164*7a9b709eSPawan Gupta            ksft.finished()
165