1 # SPDX-License-Identifier: GPL-2.0
3 # Runs UML kernel, collects output, and handles errors.
5 # Copyright (C) 2019, Google LLC.
6 # Author: Felix Guo <felixguoxiuping@gmail.com>
7 # Author: Brendan Higgins <brendanhiggins@google.com>
16 KCONFIG_PATH = '.config'
17 kunitconfig_path = '.kunitconfig'
19 class ConfigError(Exception):
20 """Represents an error trying to configure the Linux kernel."""
23 class BuildError(Exception):
24 """Represents an error trying to build the Linux kernel."""
27 class LinuxSourceTreeOperations(object):
28 """An abstraction over command line operations performed on a source tree."""
30 def make_mrproper(self):
32 subprocess.check_output(['make', 'mrproper'])
34 raise ConfigError('Could not call make command: ' + e)
35 except subprocess.CalledProcessError as e:
36 raise ConfigError(e.output)
38 def make_olddefconfig(self, build_dir):
39 command = ['make', 'ARCH=um', 'olddefconfig']
41 command += ['O=' + build_dir]
43 subprocess.check_output(command)
45 raise ConfigError('Could not call make command: ' + e)
46 except subprocess.CalledProcessError as e:
47 raise ConfigError(e.output)
49 def make(self, jobs, build_dir):
50 command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
52 command += ['O=' + build_dir]
54 subprocess.check_output(command)
56 raise BuildError('Could not call execute make: ' + e)
57 except subprocess.CalledProcessError as e:
58 raise BuildError(e.output)
60 def linux_bin(self, params, timeout, build_dir):
61 """Runs the Linux UML binary. Must be named 'linux'."""
64 linux_bin = os.path.join(build_dir, 'linux')
65 process = subprocess.Popen(
67 stdin=subprocess.PIPE,
68 stdout=subprocess.PIPE,
69 stderr=subprocess.PIPE)
70 process.wait(timeout=timeout)
74 def get_kconfig_path(build_dir):
75 kconfig_path = KCONFIG_PATH
77 kconfig_path = os.path.join(build_dir, KCONFIG_PATH)
80 class LinuxSourceTree(object):
81 """Represents a Linux kernel source tree with KUnit tests."""
84 self._kconfig = kunit_config.Kconfig()
85 self._kconfig.read_from_file(kunitconfig_path)
86 self._ops = LinuxSourceTreeOperations()
90 self._ops.make_mrproper()
91 except ConfigError as e:
96 def build_config(self, build_dir):
97 kconfig_path = get_kconfig_path(build_dir)
98 if build_dir and not os.path.exists(build_dir):
100 self._kconfig.write_to_file(kconfig_path)
102 self._ops.make_olddefconfig(build_dir)
103 except ConfigError as e:
106 validated_kconfig = kunit_config.Kconfig()
107 validated_kconfig.read_from_file(kconfig_path)
108 if not self._kconfig.is_subset_of(validated_kconfig):
109 logging.error('Provided Kconfig is not contained in validated .config!')
113 def build_reconfig(self, build_dir):
114 """Creates a new .config if it is not a subset of the .kunitconfig."""
115 kconfig_path = get_kconfig_path(build_dir)
116 if os.path.exists(kconfig_path):
117 existing_kconfig = kunit_config.Kconfig()
118 existing_kconfig.read_from_file(kconfig_path)
119 if not self._kconfig.is_subset_of(existing_kconfig):
120 print('Regenerating .config ...')
121 os.remove(kconfig_path)
122 return self.build_config(build_dir)
126 print('Generating .config ...')
127 return self.build_config(build_dir)
129 def build_um_kernel(self, jobs, build_dir):
131 self._ops.make_olddefconfig(build_dir)
132 self._ops.make(jobs, build_dir)
133 except (ConfigError, BuildError) as e:
136 used_kconfig = kunit_config.Kconfig()
137 used_kconfig.read_from_file(get_kconfig_path(build_dir))
138 if not self._kconfig.is_subset_of(used_kconfig):
139 logging.error('Provided Kconfig is not contained in final config!')
143 def run_kernel(self, args=[], timeout=None, build_dir=''):
144 args.extend(['mem=256M'])
145 process = self._ops.linux_bin(args, timeout, build_dir)
146 with open(os.path.join(build_dir, 'test.log'), 'w') as f:
147 for line in process.stdout:
148 f.write(line.rstrip().decode('ascii') + '\n')
149 yield line.rstrip().decode('ascii')