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