Files
kata-containers/kernel/build-kernel.sh
Graham Whaley 0e604bde96 kernel: configs: add kernel fragment support
Add the framework to build kernel config files from trees
of kernel fragments.

If no fragment directory is found for the requested kernel
version and architecture then revert to looking for a whole
prebuilt kernel config file instead.

Fixes: #234

Signed-off-by: Graham Whaley <graham.whaley@intel.com>
2019-07-25 18:13:03 +00:00

423 lines
12 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
description="
Description: This script is the *ONLY* to build a kernel for development.
"
set -o errexit
set -o nounset
set -o pipefail
readonly script_name="$(basename "${BASH_SOURCE[0]}")"
readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
#project_name
readonly project_name="kata-containers"
[ -n "${GOPATH:-}" ] || GOPATH="${HOME}/go"
# Fetch the first element from GOPATH as working directory
# as go get only works against the first item in the GOPATH
GOPATH="${GOPATH%%:*}"
# Kernel version to be used
kernel_version=""
# Flag know if need to download the kernel source
download_kernel=false
# The repository where kernel configuration lives
runtime_repository="github.com/${project_name}/runtime"
# The repository where kernel configuration lives
readonly kernel_config_repo="github.com/${project_name}/packaging"
readonly patches_repo="github.com/${project_name}/packaging"
readonly patches_repo_dir="${GOPATH}/src/${patches_repo}"
# Default path to search patches to apply to kernel
readonly default_patches_dir="${patches_repo_dir}/kernel/patches/"
# Default path to search config for kata
readonly default_kernel_config_dir="${GOPATH}/src/${kernel_config_repo}/kernel/configs"
# Default path to search for kernel config fragments
readonly default_config_frags_dir="${GOPATH}/src/${kernel_config_repo}/kernel/configs/fragments"
#Path to kernel directory
kernel_path=""
#
patches_path=""
#
hypervisor_target=""
#
arch_target=""
#
kernel_config_path=""
# destdir
DESTDIR="${DESTDIR:-/}"
#PREFIX=
PREFIX="${PREFIX:-/usr}"
source "${script_dir}/../scripts/lib.sh"
usage() {
cat <<EOT
Overview:
Build a kernel for Kata Containers
${description}
Usage:
$script_name [options] <command> <argument>
Commands:
- setup
- build
- install
Options:
-c <path>: Path to config file to build a the kernel
-h : Display this help.
-k <path>: Path to kernel to build
-p <path>: Path to a directory with patches to apply to kernel.
-v : Kernel version to use if kernel path not provided.
EOT
}
# Convert architecture to the name used by the Linux kernel build system
arch_to_kernel() {
local -r arch="$1"
case "$arch" in
aarch64) echo "arm64" ;;
ppc64le) echo "powerpc" ;;
x86_64) echo "$arch" ;;
s390x) echo "s390" ;;
*) die "unsupported architecture: $arch" ;;
esac
}
get_kernel() {
local version="${1:-}"
#Remove extra 'v'
version=${version#v}
local kernel_path=${2:-}
[ -n "${kernel_path}" ] || die "kernel_path not provided"
[ ! -d "${kernel_path}" ] || die "kernel_path already exist"
major_version=$(echo "${version}" | cut -d. -f1)
kernel_tarball="linux-${version}.tar.xz"
curl --fail -OL "https://cdn.kernel.org/pub/linux/kernel/v${major_version}.x/sha256sums.asc"
grep "${kernel_tarball}" sha256sums.asc >"${kernel_tarball}.sha256"
if [ -f "${kernel_tarball}" ] && ! sha256sum -c "${kernel_tarball}.sha256"; then
info "invalid kernel tarball ${kernel_tarball} removing "
rm -f "${kernel_tarball}"
fi
if [ ! -f "${kernel_tarball}" ]; then
info "Download kernel version ${version}"
info "Download kernel"
curl --fail -OL "https://www.kernel.org/pub/linux/kernel/v${major_version}.x/${kernel_tarball}"
else
info "kernel tarball already downloaded"
fi
sha256sum -c "${kernel_tarball}.sha256"
tar xf "${kernel_tarball}"
mv "linux-${version}" "${kernel_path}"
}
get_major_kernel_version() {
local version="${1}"
[ -n "${version}" ] || die "kernel version not provided"
major_version=$(echo "${version}" | cut -d. -f1)
minor_version=$(echo "${version}" | cut -d. -f2)
echo "${major_version}.${minor_version}"
}
# Make a kernel config file from generic and arch specific
# fragments
# - arg1 - path to arch specific fragments
# - arg2 - path to kernel sources
#
get_kernel_frag_path() {
local arch_path="$1"
local common_path="${arch_path}/../common"
local kernel_path="$2"
local cmdpath="${kernel_path}/scripts/kconfig/merge_config.sh"
local config_path="${arch_path}/.config"
local arch_configs="$(ls ${arch_path}/*.conf)"
local common_configs="$(ls ${common_path}/*.conf)"
# These are the strings that the kernel merge_config.sh script kicks out
# when it reports an error or warning condition. We search for them in the
# output to try and fail when we think something has been misconfigured.
local not_in_string="not in final"
local redefined_string="not in final"
local redundant_string="not in final"
# Later, if we need to add kernel version specific subdirs in order to
# handle specific cases, then add the path definition and search/list/cat
# here.
local all_configs="${common_configs} ${arch_configs}"
info "Constructing config from fragments: ${config_path}"
local results=$(export KCONFIG_CONFIG=${config_path}; \
export ARCH=${arch_target}; \
cd ${kernel_path}; ${cmdpath} -r -n ${all_configs})
# Did we request any entries that did not make it?
local missing=$(echo $results | grep -v -q "${not_in_string}"; echo $?)
if [ ${missing} -ne 0 ]; then
info "Some CONFIG elements failed to make the final .config:"
info "${results}"
info "Generated config file can be found in ${config_path}"
die "Failed to construct requested .config file"
fi
# Did we define something as two different values?
local redefined=$(echo ${results} | grep -v -q "${redefined_string}"; echo $?)
if [ ${redefined} -ne 0 ]; then
info "Some CONFIG elements are redefined in fragments:"
info "${results}"
info "Generated config file can be found in ${config_path}"
die "Failed to construct requested .config file"
fi
# Did we define something twice? Nominally this may not be an error, and it
# might be convenient to allow it, but for now, let's pick up on them.
local redundant=$(echo ${results} | grep -v -q "${redundant_string}"; echo $?)
if [ ${redundant} -ne 0 ]; then
info "Some CONFIG elements failed to make the final .config"
info "${results}"
info "Generated config file can be found in ${config_path}"
die "Failed to construct requested .config file"
fi
echo "${config_path}"
}
# Locate and return the path to the relevant kernel config file
# - arg1: kernel version
# - arg2: hypervisor target
# - arg3: arch target
# - arg4: kernel source path
get_default_kernel_config() {
local version="${1}"
local hypervisor="$2"
local kernel_arch="$3"
local kernel_path="$4"
[ -n "${version}" ] || die "kernel version not provided"
[ -n "${hypervisor}" ] || die "hypervisor not provided"
[ -n "${kernel_arch}" ] || die "kernel arch not provided"
local kernel_ver
kernel_ver=$(get_major_kernel_version "${version}")
archfragdir="${default_config_frags_dir}/${kernel_arch}"
if [ -d "${archfragdir}" ]; then
config="$(get_kernel_frag_path ${archfragdir} ${kernel_path})"
else
config="${default_kernel_config_dir}/${kernel_arch}_kata_${hypervisor}_${major_kernel}.x"
fi
[ -f "${config}" ] || die "failed to find default config ${config}"
echo "${config}"
}
get_config_and_patches() {
if [ -z "${patches_path}" ]; then
info "Clone config and patches"
patches_path="${default_patches_dir}"
[ -d "${patches_path}" ] || git clone "https://${patches_repo}.git" "${patches_repo_dir}"
fi
}
get_config_version() {
get_config_and_patches
config_version_file="${default_patches_dir}/../kata_config_version"
if [ -f "${config_version_file}" ]; then
cat "${config_version_file}"
else
die "failed to find ${config_version_file}"
fi
}
setup_kernel() {
local kernel_path=${1:-}
[ -n "${kernel_path}" ] || die "kernel_path not provided"
if [ -d "$kernel_path" ]; then
info "${kernel_path} already exist"
return
fi
info "kernel path does not exist, will download kernel"
download_kernel="true"
[ -n "$kernel_version" ] || die "failed to get kernel version: Kernel version is emtpy"
if [[ ${download_kernel} == "true" ]]; then
get_kernel "${kernel_version}" "${kernel_path}"
fi
[ -n "$kernel_path" ] || die "failed to find kernel source path"
get_config_and_patches
[ -d "${patches_path}" ] || die " patches path '${patches_path}' does not exist"
local major_kernel
major_kernel=$(get_major_kernel_version "${kernel_version}")
local patches_dir_for_version="${patches_path}/${major_kernel}.x"
local kernel_patches=""
if [ -d "${patches_dir_for_version}" ]; then
kernel_patches=$(find "${patches_dir_for_version}" -name '*.patch' -type f)
else
info "kernel patches directory does not exit"
fi
[ -n "${arch_target}" ] || arch_target="$(uname -m)"
arch_target=$(arch_to_kernel "${arch_target}")
(
cd "${kernel_path}" || exit 1
for p in ${kernel_patches}; do
info "Applying patch $p"
patch -p1 --fuzz 0 <"$p"
done
[ -n "${hypervisor_target}" ] || hypervisor_target="kvm"
[ -n "${kernel_config_path}" ] || kernel_config_path=$(get_default_kernel_config "${kernel_version}" "${hypervisor_target}" "${arch_target}" "${kernel_path}")
info "Copying config file from: ${kernel_config_path}"
cp "${kernel_config_path}" ./.config
make oldconfig
)
}
build_kernel() {
local kernel_path=${1:-}
[ -n "${kernel_path}" ] || die "kernel_path not provided"
[ -d "${kernel_path}" ] || die "path to kernel does not exist, use ${script_name} setup"
[ -n "${arch_target}" ] || arch_target="$(uname -m)"
arch_target=$(arch_to_kernel "${arch_target}")
pushd "${kernel_path}" >>/dev/null
make -j $(nproc) ARCH="${arch_target}"
[ "$arch_target" != "powerpc" ] && ([ -e "arch/${arch_target}/boot/bzImage" ] || [ -e "arch/${arch_target}/boot/Image.gz" ])
[ -e "vmlinux" ]
popd >>/dev/null
}
install_kata() {
local kernel_path=${1:-}
[ -n "${kernel_path}" ] || die "kernel_path not provided"
[ -d "${kernel_path}" ] || die "path to kernel does not exist, use ${script_name} setup"
pushd "${kernel_path}" >>/dev/null
config_version=$(get_config_version)
[ -n "${config_version}" ] || die "failed to get config version"
install_path=$(readlink -m "${DESTDIR}/${PREFIX}/share/${project_name}")
vmlinuz="vmlinuz-${kernel_version}-${config_version}"
vmlinux="vmlinux-${kernel_version}-${config_version}"
if [ -e "arch/${arch_target}/boot/bzImage" ]; then
bzImage="arch/${arch_target}/boot/bzImage"
elif [ -e "arch/${arch_target}/boot/Image.gz" ]; then
bzImage="arch/${arch_target}/boot/Image.gz"
elif [ "${arch_target}" != "powerpc" ]; then
die "failed to find image"
fi
if [ "${arch_target}" = "powerpc" ]; then
install --mode 0644 -D "vmlinux" "${install_path}/${vmlinuz}"
else
install --mode 0644 -D "${bzImage}" "${install_path}/${vmlinuz}"
fi
install --mode 0644 -D "vmlinux" "${install_path}/${vmlinux}"
install --mode 0644 -D ./.config "${install_path}/config-${kernel_version}"
ln -sf "${vmlinuz}" "${install_path}/vmlinuz.container"
ln -sf "${vmlinux}" "${install_path}/vmlinux.container"
ls -la "${install_path}/vmlinux.container"
ls -la "${install_path}/vmlinuz.container"
popd >>/dev/null
}
main() {
while getopts "a:c:hk:p:t:v:" opt; do
case "$opt" in
a)
arch_target="${OPTARG}"
;;
c)
kernel_config_path="${OPTARG}"
;;
h)
usage
exit 0
;;
k)
kernel_path="${OPTARG}"
;;
t)
hypervisor_target="${OPTARG}"
;;
p)
patches_path="${OPTARG}"
;;
v)
kernel_version="${OPTARG}"
;;
esac
done
shift $((OPTIND - 1))
subcmd="${1:-}"
[ -z "${subcmd}" ] && usage 1
# If not kernel version take it from versions.yaml
if [ -z "$kernel_version" ]; then
kernel_version=$(get_from_kata_deps "assets.kernel.version")
#Remove extra 'v'
kernel_version="${kernel_version#v}"
fi
if [ -z "${kernel_path}" ]; then
config_version=$(get_config_version)
kernel_path="${PWD}/kata-linux-${kernel_version}-${config_version}"
info "Config version: ${config_version}"
fi
info "Kernel version: ${kernel_version}"
case "${subcmd}" in
build)
build_kernel "${kernel_path}"
;;
install)
build_kernel "${kernel_path}"
install_kata "${kernel_path}"
;;
setup)
setup_kernel "${kernel_path}"
[ -d "${kernel_path}" ] || die "${kernel_path} does not exist"
echo "Kernel source ready: ${kernel_path} "
;;
*)
usage 1
;;
esac
}
main $@