xref: /aosp_15_r20/external/autotest/client/cros/kernel_config.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Liimport gzip, logging, os, re
7*9c5db199SXin Lifrom autotest_lib.client.bin import utils
8*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
9*9c5db199SXin Li
10*9c5db199SXin Liclass KernelConfig():
11*9c5db199SXin Li    """
12*9c5db199SXin Li    Parse the kernel config and enable us to query it.
13*9c5db199SXin Li    Used to verify the kernel config (see kernel_ConfigVerify).
14*9c5db199SXin Li    """
15*9c5db199SXin Li
16*9c5db199SXin Li    def _passed(self, msg):
17*9c5db199SXin Li        logging.info('ok: %s', msg)
18*9c5db199SXin Li
19*9c5db199SXin Li    def _failed(self, msg):
20*9c5db199SXin Li        logging.error('FAIL: %s', msg)
21*9c5db199SXin Li        self._failures.append(msg)
22*9c5db199SXin Li
23*9c5db199SXin Li    def failures(self):
24*9c5db199SXin Li        """Return the list of failures that occured during the test.
25*9c5db199SXin Li
26*9c5db199SXin Li        @return a list of string describing errors that occured since
27*9c5db199SXin Li                initialization.
28*9c5db199SXin Li        """
29*9c5db199SXin Li        return self._failures
30*9c5db199SXin Li
31*9c5db199SXin Li    def _fatal(self, msg):
32*9c5db199SXin Li        logging.error('FATAL: %s', msg)
33*9c5db199SXin Li        raise error.TestError(msg)
34*9c5db199SXin Li
35*9c5db199SXin Li    def get(self, key, default):
36*9c5db199SXin Li        """Get the value associated to key or default if it does not exist
37*9c5db199SXin Li
38*9c5db199SXin Li        @param key: key to look for.
39*9c5db199SXin Li        @param default: value returned if key is not set in self._config
40*9c5db199SXin Li        """
41*9c5db199SXin Li        return self._config.get(key, default)
42*9c5db199SXin Li
43*9c5db199SXin Li    def _config_required(self, name, wanted):
44*9c5db199SXin Li        value = self._config.get(name, None)
45*9c5db199SXin Li        if value in wanted:
46*9c5db199SXin Li            self._passed('"%s" was "%s" in kernel config' % (name, value))
47*9c5db199SXin Li        else:
48*9c5db199SXin Li            states = []
49*9c5db199SXin Li            for state in wanted:
50*9c5db199SXin Li                if state == None:
51*9c5db199SXin Li                    states.append("unset")
52*9c5db199SXin Li                else:
53*9c5db199SXin Li                    states.append(state)
54*9c5db199SXin Li            self._failed('"%s" was "%s" (wanted one of "%s") in kernel config' %
55*9c5db199SXin Li                         (name, value, '|'.join(states)))
56*9c5db199SXin Li
57*9c5db199SXin Li    def has_value(self, name, value):
58*9c5db199SXin Li        """Determine if the name config item has a specific value.
59*9c5db199SXin Li
60*9c5db199SXin Li        @param name: name of config item to test
61*9c5db199SXin Li        @param value: value expected for the given config name
62*9c5db199SXin Li        """
63*9c5db199SXin Li        self._config_required('CONFIG_%s' % (name), value)
64*9c5db199SXin Li
65*9c5db199SXin Li    def has_builtin(self, name):
66*9c5db199SXin Li        """Check if the specific config item is built-in (present but not
67*9c5db199SXin Li        built as a module).
68*9c5db199SXin Li
69*9c5db199SXin Li        @param name: name of config item to test
70*9c5db199SXin Li        """
71*9c5db199SXin Li        wanted = ['y']
72*9c5db199SXin Li        if name in self._missing_ok:
73*9c5db199SXin Li            wanted.append(None)
74*9c5db199SXin Li        self.has_value(name, wanted)
75*9c5db199SXin Li
76*9c5db199SXin Li    def has_module(self, name):
77*9c5db199SXin Li        """Check if the specific config item is a module (present but not
78*9c5db199SXin Li        built-in).
79*9c5db199SXin Li
80*9c5db199SXin Li        @param name: name of config item to test
81*9c5db199SXin Li        """
82*9c5db199SXin Li        wanted = ['m']
83*9c5db199SXin Li        if name in self._missing_ok:
84*9c5db199SXin Li            wanted.append(None)
85*9c5db199SXin Li        self.has_value(name, wanted)
86*9c5db199SXin Li
87*9c5db199SXin Li    def is_enabled(self, name):
88*9c5db199SXin Li        """Check if the specific config item is present (either built-in or
89*9c5db199SXin Li        a module).
90*9c5db199SXin Li
91*9c5db199SXin Li        @param name: name of config item to test
92*9c5db199SXin Li        """
93*9c5db199SXin Li        wanted = ['y', 'm']
94*9c5db199SXin Li        if name in self._missing_ok:
95*9c5db199SXin Li            wanted.append(None)
96*9c5db199SXin Li        self.has_value(name, wanted)
97*9c5db199SXin Li
98*9c5db199SXin Li    def is_missing(self, name):
99*9c5db199SXin Li        """Check if the specific config item is not present (neither built-in
100*9c5db199SXin Li        nor a module).
101*9c5db199SXin Li
102*9c5db199SXin Li        @param name: name of config item to test
103*9c5db199SXin Li        """
104*9c5db199SXin Li        self.has_value(name, [None])
105*9c5db199SXin Li
106*9c5db199SXin Li    def is_exclusive(self, exclusive):
107*9c5db199SXin Li        """Given a config item regex, make sure only the expected items
108*9c5db199SXin Li        are present in the kernel configs.
109*9c5db199SXin Li
110*9c5db199SXin Li        @param exclusive: hash containing "missing", "builtin", "module",
111*9c5db199SXin Li                          "enabled" each to be checked with the corresponding
112*9c5db199SXin Li                          has_* function based on config items matching the
113*9c5db199SXin Li                          "regex" value.
114*9c5db199SXin Li        """
115*9c5db199SXin Li        expected = set()
116*9c5db199SXin Li        for name in exclusive['missing']:
117*9c5db199SXin Li            self.is_missing(name)
118*9c5db199SXin Li        for name in exclusive['builtin']:
119*9c5db199SXin Li            self.has_builtin(name)
120*9c5db199SXin Li            expected.add('CONFIG_%s' % (name))
121*9c5db199SXin Li        for name in exclusive['module']:
122*9c5db199SXin Li            self.has_module(name)
123*9c5db199SXin Li            expected.add('CONFIG_%s' % (name))
124*9c5db199SXin Li        for name in exclusive['enabled']:
125*9c5db199SXin Li            self.is_enabled(name)
126*9c5db199SXin Li            expected.add('CONFIG_%s' % (name))
127*9c5db199SXin Li
128*9c5db199SXin Li        # Now make sure nothing else with the specified regex exists.
129*9c5db199SXin Li        regex = r'CONFIG_%s' % (exclusive['regex'])
130*9c5db199SXin Li        for name in self._config:
131*9c5db199SXin Li            if not re.match(regex, name):
132*9c5db199SXin Li                continue
133*9c5db199SXin Li            if not name in expected:
134*9c5db199SXin Li                self._failed('"%s" found for "%s" when only "%s" allowed' %
135*9c5db199SXin Li                             (name, regex, "|".join(expected)))
136*9c5db199SXin Li
137*9c5db199SXin Li    def _read_config(self):
138*9c5db199SXin Li        """Open the kernel's build config file. Attempt to use the built-in
139*9c5db199SXin Li        symbols from /proc first, then fall back to looking for a text file
140*9c5db199SXin Li        in /boot.
141*9c5db199SXin Li
142*9c5db199SXin Li        @return readlines for fileobj
143*9c5db199SXin Li        """
144*9c5db199SXin Li        filename = '/proc/config.gz'
145*9c5db199SXin Li        if not os.path.exists(filename):
146*9c5db199SXin Li            utils.system("modprobe configs", ignore_status=True)
147*9c5db199SXin Li        if os.path.exists(filename):
148*9c5db199SXin Li            with gzip.open(filename, "rt") as rf:
149*9c5db199SXin Li                return rf.readlines()
150*9c5db199SXin Li
151*9c5db199SXin Li        filename = '/boot/config-%s' % utils.system_output('uname -r')
152*9c5db199SXin Li        if os.path.exists(filename):
153*9c5db199SXin Li            logging.info('Falling back to reading %s', filename)
154*9c5db199SXin Li            with open(filename, "r") as rf:
155*9c5db199SXin Li                return rf.readlines()
156*9c5db199SXin Li
157*9c5db199SXin Li        self._fatal("Cannot locate suitable kernel config file")
158*9c5db199SXin Li
159*9c5db199SXin Li    def initialize(self, missing_ok=None):
160*9c5db199SXin Li        """Load the kernel configuration and parse it.
161*9c5db199SXin Li        """
162*9c5db199SXin Li        file_lines = self._read_config()
163*9c5db199SXin Li        # Import kernel config variables into a dictionary for each searching.
164*9c5db199SXin Li        config = dict()
165*9c5db199SXin Li        for item in file_lines:
166*9c5db199SXin Li            item = item.strip()
167*9c5db199SXin Li            if not '=' in item:
168*9c5db199SXin Li                continue
169*9c5db199SXin Li            key, value = item.split('=', 1)
170*9c5db199SXin Li            config[key] = value
171*9c5db199SXin Li
172*9c5db199SXin Li        # Make sure we actually loaded something sensible.
173*9c5db199SXin Li        if len(config) == 0:
174*9c5db199SXin Li            self._fatal('No CONFIG variables found!')
175*9c5db199SXin Li
176*9c5db199SXin Li        self._config = config
177*9c5db199SXin Li        self._failures = []
178*9c5db199SXin Li        self._missing_ok = set()
179*9c5db199SXin Li        if missing_ok:
180*9c5db199SXin Li            self._missing_ok |= set(missing_ok)
181