blob: a1d4c3d089275081f19177ebfda22a090962bfd1 [file] [log] [blame]
# Copyright 2020 The Monogon Project Authors.
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Rules for building Linux kernel images.
This currently performs the build in a fully unhermetic manner, using
make/gcc/... from the sandbox sysroot, and is only slightly better than a genrule. This
should be replaced by a hermetic build that at least uses rules_cc toolchain
information, or even better, just uses cc_library targets.
"""
load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
load("@rules_cc//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_ATTRS", "find_cpp_toolchain", "use_cc_toolchain")
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
load("//build/utils:detect_root.bzl", "detect_root")
load("//osbase/build:def.bzl", "ignore_unused_configuration")
def _linux_image_impl_resources(_os, _ninputs):
"""
Configures linux build resources.
See `resource_set` documentation in builtins.actions Bazel docs.
"""
# 16 threads seems about right - this fits well in both our build machines and
# development machines.
cpu = 16
# In MB. Picked based on observing build in htop.
mb_per_cpu = 256
return {
"cpu": cpu,
"memory": cpu * mb_per_cpu,
"local_test": 0,
}
DISABLED_FEATURES = []
# NOTE: Multicall tool is called as path/to/llvm clang to workaround bug in out-of-process execution where tool name is repeated and parsing breaks.
def _linux_image_impl(ctx):
kernel_config = ctx.file.kernel_config
kernel_src = ctx.files.kernel_src
image_format = ctx.attr.image_format
# Root of the given Linux sources.
root = detect_root(ctx.attr.kernel_src)
# Figure out target CC toolchain
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = DISABLED_FEATURES + ctx.disabled_features,
)
c_compiler_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = C_COMPILE_ACTION_NAME,
)
# Figure out Kbuild ARCH option
target_arch = None
compressed_image_name = None
if ctx.target_platform_has_constraint(ctx.attr._constraint_x86_64[platform_common.ConstraintValueInfo]):
target_arch = "x86"
compressed_image_name = "bzImage"
if ctx.target_platform_has_constraint(ctx.attr._constraint_aarch64[platform_common.ConstraintValueInfo]):
target_arch = "arm64"
compressed_image_name = "Image"
if ctx.target_platform_has_constraint(ctx.attr._constraint_riscv64[platform_common.ConstraintValueInfo]):
target_arch = "riscv"
compressed_image_name = "Image"
if not target_arch:
fail("Target platform does not match expected constraints: @platforms//cpu:x86_64, @platforms//cpu:aarch64, or @platforms//cpu:riscv64.")
# Tuple containing information about how to build and access the resulting
# image.
# The first element (target) is the make target to build, the second
# (image_source) is the resulting file to be copied and the last
# (image_name) is the name of the image that will be generated by this
# rule.
(target, image_source, image_name) = {
"vmlinux": ("vmlinux modules", "vmlinux", "vmlinux"),
"Image": ("all modules", "arch/" + target_arch + "/boot/" + compressed_image_name, "Image"),
}[image_format]
image = ctx.actions.declare_file(image_name)
modinfo = ctx.actions.declare_file("modules.builtin.modinfo")
modules = ctx.actions.declare_directory("modules")
ctx.actions.run_shell(
outputs = [image, modinfo, modules],
inputs = depset([kernel_config] + kernel_src, transitive = [cc_toolchain.all_files]),
resource_set = _linux_image_impl_resources,
command = '''
kconfig=$1
target=$2
image_source=$3
image=$4
root=$5
modinfo=$6
modules=$7
arch=$8
cc=$PWD/$9
builddir=$(mktemp -d)
mkdir ${root}/.bin
cp ${kconfig} ${builddir}/.config
(cd ${root} && make -j 16 KBUILD_OUTPUT="${builddir}" ARCH="${arch}" CC="${cc//clang/llvm} clang" LD="${cc//clang/ld.lld}" OBJCOPY="${cc//clang/llvm-objcopy}" OBJDUMP="${cc//clang/llvm-objdump}" AR="${cc//clang/llvm-ar}" NM="${cc//clang/llvm-nm}" STRIP="${cc//clang/llvm-strip}" READELF="${cc//clang/llvm-readelf}" olddefconfig ${target} >/dev/null)
cp "${builddir}"/${image_source} ${image}
cp "${builddir}"/modules.builtin.modinfo ${modinfo}
# Not using modules_install as it tries to run depmod and friends
for f in $(find "${builddir}" -name '*.ko' -type f -printf "%P\n" ); do
install -D "${builddir}/$f" "${modules}/$f"
done
rm -Rf "$builddir"
''',
arguments = [
kernel_config.path,
target,
image_source,
image.path,
root,
modinfo.path,
modules.path,
target_arch,
c_compiler_path,
],
use_default_shell_env = True,
)
return [
DefaultInfo(
files = depset([image]),
runfiles = ctx.runfiles(files = [image]),
),
OutputGroupInfo(
modinfo = depset([modinfo]),
modules = depset([modules]),
),
]
linux_image = rule(
doc = """
Build Linux kernel image unhermetically in a given format.
""",
implementation = _linux_image_impl,
cfg = ignore_unused_configuration,
attrs = {
"kernel_config": attr.label(
doc = """
Linux kernel configuration file to build this kernel image with.
""",
allow_single_file = True,
),
"kernel_src": attr.label(
doc = """
Filegroup containing Linux kernel sources.
""",
default = "@linux//:all",
),
"image_format": attr.string(
doc = """
Format of generated Linux image, one of 'vmlinux' or 'Image',
""",
values = [
"vmlinux",
"Image",
],
default = "Image",
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
# Bazel doesn't let you access the target platform directly, use these
"_constraint_x86_64": attr.label(
default = "@platforms//cpu:x86_64",
),
"_constraint_aarch64": attr.label(
default = "@platforms//cpu:aarch64",
),
"_constraint_riscv64": attr.label(
default = "@platforms//cpu:riscv64",
),
} | CC_TOOLCHAIN_ATTRS,
toolchains = use_cc_toolchain(),
fragments = ["cpp"],
)