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')