417 lines
11 KiB
Bash
Executable File
417 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# DEBUG
|
|
if [ ! -z "$DEBUG" ]; then
|
|
echo "DEBUG is set"
|
|
set -o xtrace
|
|
fi
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
|
|
# shellcheck source=HW5/config.sh
|
|
source "${SCRIPT_DIR}/config.sh"
|
|
|
|
function download() {
|
|
local -r url="${1}"
|
|
local -r sha256="${2}"
|
|
|
|
if which nix-prefetch-url >/dev/null 2>&1; then
|
|
nix-prefetch-url --print-path --type sha256 "${url}" "${sha256}" | tail -n 1
|
|
else
|
|
local -r tmpdir="$(mktemp -d)"
|
|
cd "${tmpdir}" && local -r filename="$(curl "${url}" --remote-name -w "%{filename_effective}")"
|
|
echo "${tmpdir}/${filename}"
|
|
fi
|
|
}
|
|
|
|
function download_extract() {
|
|
local -r url="${1}"
|
|
local -r sha256="${2}"
|
|
local -r path="${3}"
|
|
|
|
echo "Downloading '${url}'..."
|
|
local -r archive="$(download "${url}" "${sha256}")"
|
|
|
|
if [ -z "$sha256" ]; then
|
|
echo "Verify checksum of '${archive}'..."
|
|
if [ "$(sha256sum "${archive}" | cut -d' ' -f1)" != "${sha256}" ]; then
|
|
echo 'Error: Invalid checksum!'
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
echo "Extracting '${archive}'..."
|
|
tar -xf "${archive}" -C "${path}" || return $?
|
|
}
|
|
|
|
function download_extract_all() {
|
|
download_extract "${KERNEL_URL}" "${KERNEL_SHA256}" "${KERNEL_PATH}" || return $?
|
|
download_extract "${BUSYBOX_URL}" "${BUSYBOX_SHA256}" "${BUSYBOX_PATH}" || return $?
|
|
download_extract "${DROPBEAR_URL}" "${DROPBEAR_SHA256}" "${DROPBEAR_PATH}" || return $?
|
|
download_extract "${GESFTPSERVER_URL}" "${GESFTPSERVER_SHA256}" "${GESFTPSERVER_PATH}" || return $?
|
|
}
|
|
|
|
function cpu_count() {
|
|
if [ -f /proc/cpuinfo ]; then
|
|
grep -c ^processor /proc/cpuinfo
|
|
else
|
|
if which sysctl >/dev/null 2>&1; then
|
|
sysctl hw.ncpu | cut -d ' ' -f 2
|
|
else
|
|
echo '4'
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# compile kernel
|
|
function compile_kernel() {
|
|
ln -sf "${KERNEL_CONFIG_PATH}" "${KERNEL_SOURCE_PATH}/.config"
|
|
echo "Compile kernel..."
|
|
time ARCH="$KERNEL_ARCH" make -C "${KERNEL_SOURCE_PATH}" -j "$(cpu_count)" || return $?
|
|
echo "Copy kernel..."
|
|
cp -pf "${KERNEL_IMAGE}" "${ARTIFACTS_PATH}/"
|
|
}
|
|
|
|
# compile busybox
|
|
function compile_busybox() {
|
|
ln -sf "${BUSYBOX_CONFIG_PATH}" "${BUSYBOX_SOURCE_PATH}/.config"
|
|
echo "Compile busybox..."
|
|
time make -C "${BUSYBOX_SOURCE_PATH}" -j "$(cpu_count)" || return $?
|
|
echo "Copy busybox..."
|
|
cp -pf "${BUSYBOX_BIN}" "${ARTIFACTS_PATH}/"
|
|
echo "Copy libs and patch ld path..."
|
|
build_binary "${BUSYBOX_BIN}" || return $?
|
|
}
|
|
|
|
# compile dropbear
|
|
function compile_dropbear() {
|
|
ln -sf "${DROPBEAR_CONFIG_PATH}" "${DROPBEAR_SOURCE_PATH}/options.h"
|
|
echo "Configure dropbear..."
|
|
cd "${DROPBEAR_SOURCE_PATH}" && CC=${CROSS_COMPILE}gcc LD=${CROSS_COMPILE}ld ./configure --host=aarch64-linux-gnu --disable-lastlog --disable-syslog --disable-wtmp --disable-wtmpx --disable-utmpx || return $?
|
|
echo "Compile dropbear..."
|
|
time make -C "${DROPBEAR_SOURCE_PATH}" -j "$(cpu_count)" PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" MULTI=1 || return $?
|
|
echo "Copy dropbear..."
|
|
cp -pf "${DROPBEAR_BIN}" "${ARTIFACTS_PATH}/"
|
|
echo "Copy libs and patch ld path..."
|
|
build_binary "${DROPBEAR_BIN}" || return $?
|
|
}
|
|
|
|
# compile gesftpserver
|
|
function compile_gesftpserver() {
|
|
echo "Configure gesftpserver..."
|
|
cd "${GESFTPSERVER_SOURCE_PATH}" && CC=${CROSS_COMPILE}gcc LD=${CROSS_COMPILE}ld CFLAGS=-pthread ./configure --host=aarch64-linux-gnu || return $?
|
|
echo "Compile gesftpserver..."
|
|
time make -C "${GESFTPSERVER_SOURCE_PATH}" -j "$(cpu_count)" gesftpserver || return $?
|
|
echo "Copy gesftpserver..."
|
|
cp -pf "${GESFTPSERVER_BIN}" "${ARTIFACTS_PATH}/"
|
|
echo "Copy libs and patch ld path..."
|
|
# TODO: ld.so with qemu-aarch64 does not work for gesftpserver
|
|
#build_binary "${GESFTPSERVER_BIN}"
|
|
patch_binary "${GESFTPSERVER_BIN}" || return $?
|
|
copy_libs 'libc.so.6' 'libpthread.so.0' || return $?
|
|
}
|
|
|
|
# compile sysinfo
|
|
function compile_sysinfo() {
|
|
echo "Compile sysinfo..."
|
|
time CC=${CROSS_COMPILE}gcc LD=${CROSS_COMPILE}ld make -C "${SYSINFO_SOURCE_PATH}" -j "$(cpu_count)" || return $?
|
|
echo "Copy sysinfo..."
|
|
cp -pf "${SYSINFO_BIN}" "${ARTIFACTS_PATH}/"
|
|
echo "Copy libs and patch ld path..."
|
|
build_binary "${SYSINFO_BIN}" || return $?
|
|
}
|
|
|
|
function compile_module() {
|
|
local -r module_dir="$1"
|
|
local -r module="$(basename "$module_dir")"
|
|
local -r module_test_src="${module_dir}/${module}.test.c"
|
|
|
|
echo "Compile kernel module '$module'..."
|
|
time KVER="$KERNEL_VERSION" make -C "${module_dir}" -j "$(cpu_count)" || return $?
|
|
|
|
if [ -f "${module_test_src}" ]; then
|
|
time make -C "${module_dir}" -j "$(cpu_count)" test || return $?
|
|
fi
|
|
|
|
echo "Copy kernel module '$module'..."
|
|
cp -pf "${module_dir}/${module}.ko" "$MODULES_DST_DIR/"
|
|
cp -pf "${module_dir}/${module}.ko.test" "$MODULES_DST_DIR/"
|
|
|
|
if [ -f "${module_test_src}" ]; then
|
|
build_binary "${module_dir}/${module}.ko.test" "$(basename "$MODULES_DST_DIR")/" || return $?
|
|
fi
|
|
}
|
|
|
|
function modules_compile() {
|
|
mkdir -p "$MODULES_DST_DIR"
|
|
|
|
while IFS= read -r -d '' module_dir; do
|
|
compile_module "$module_dir" || return $?
|
|
done < <(find_modules)
|
|
}
|
|
|
|
function compile() {
|
|
compile_kernel || return $?
|
|
compile_busybox || return $?
|
|
compile_dropbear || return $?
|
|
compile_gesftpserver || return $?
|
|
compile_sysinfo || return $?
|
|
}
|
|
|
|
function get_binary_linker() {
|
|
local -r binary="$1"
|
|
file "$binary" | grep -o -E ', interpreter (.*\.so(\.[0-9]+)?)' | cut -d' ' -f3
|
|
}
|
|
|
|
function copy_lib() {
|
|
cp -pf "$1" "${ARTIFACTS_PATH}/lib/" || return $?
|
|
}
|
|
|
|
# copy binary libs
|
|
function copy_binary_libs() {
|
|
local -r binary="$1"
|
|
|
|
echo "Copy libs for '$binary'..."
|
|
mkdir -p "${ARTIFACTS_PATH}/lib"
|
|
|
|
local -r linker="$(get_binary_linker "$binary")"
|
|
local -r libs="$(qemu-aarch64 "$linker" --list "$binary" | sed -e 's/[^\t].* => //' | sed -e 's/^\s//' | cut -d' ' -f1)"
|
|
|
|
while read -r lib; do
|
|
copy_lib "$lib" || return $?
|
|
done <<< "$libs"
|
|
}
|
|
|
|
# copy libs
|
|
function copy_libs() {
|
|
echo "Copy libs..."
|
|
mkdir -p "${ARTIFACTS_PATH}/lib"
|
|
for lib in "$@"; do
|
|
copy_lib "$(${CROSS_COMPILE}gcc "-print-file-name=$lib")" || return $?
|
|
done
|
|
}
|
|
|
|
# patch lib paths
|
|
function patch_binary() {
|
|
local -r binary="$1"
|
|
echo "Patching lib paths for '$binary'..."
|
|
local -r linker="$(basename "$(get_binary_linker "$binary")")"
|
|
patchelf --set-interpreter "/lib/$linker" --set-rpath '/lib' "$binary" || return $?
|
|
}
|
|
|
|
# do magic for binaries
|
|
function build_binary(){
|
|
local -r binary="$1"
|
|
local -r binary_dst="${ARTIFACTS_PATH}/${2}$(basename "$binary")"
|
|
patch_binary "$binary_dst" || return $?
|
|
copy_binary_libs "$binary" || return $?
|
|
}
|
|
|
|
# copy drobear libs
|
|
function copy_dropbear_libs() {
|
|
local -r libs=(libnss_compat.so.2 libnss_files.so.2)
|
|
copy_libs "${libs[@]}" || return $?
|
|
}
|
|
|
|
# create dropbear rsa key
|
|
function build_dropbear_rsa_key() {
|
|
local -r priv_key="${ARTIFACTS_PATH}/${1:-id_dropbear}"
|
|
local -r priv_key_openssh="${2}"
|
|
local -r pub_key="${priv_key}.pub"
|
|
|
|
# create private key
|
|
if [ ! -f "${priv_key}" ]; then
|
|
echo "Create dropbear private key..."
|
|
qemu-aarch64 "${DROPBEAR_BIN}" dropbearkey -t rsa -s 4096 -f "${priv_key}" || return $?
|
|
rm -f "${pub_key}"
|
|
fi
|
|
|
|
# convert private key to openssh
|
|
if [ ! -z "$priv_key_openssh" ] && [ ! -f "${priv_key_openssh}" ]; then
|
|
qemu-aarch64 "${DROPBEAR_BIN}" dropbearconvert dropbear openssh "${priv_key}" "${priv_key_openssh}" || return $?
|
|
fi
|
|
|
|
|
|
# create public key
|
|
if [ ! -f "${pub_key}" ]; then
|
|
echo "Extract dropbear public key..."
|
|
qemu-aarch64 "${DROPBEAR_BIN}" dropbearkey -f "${priv_key}" -y | tail -n 2 | head -n 1 > "${pub_key}" || return $?
|
|
fi
|
|
}
|
|
|
|
# build dropbear files
|
|
function build_dropbear() {
|
|
copy_dropbear_libs || return $?
|
|
build_dropbear_rsa_key id_dropbear "${SSH_KEY}" || return $?
|
|
build_dropbear_rsa_key dropbear_rsa_host_key || return $?
|
|
|
|
update_known_hosts || return $?
|
|
}
|
|
|
|
# build initrd
|
|
function build_initrd() {
|
|
local -r name="${1:-initrd}"
|
|
local -r root="${SCRIPT_DIR}/${name}"
|
|
local -r output="${ARTIFACTS_PATH}/${name}.cpio"
|
|
|
|
echo "Update libs in initrd..."
|
|
rm -Rf "${root:?}/lib/"
|
|
mkdir -p "${root}/lib/"
|
|
while IFS= read -r -d '' lib; do
|
|
ln -s "$lib" "${root}/lib/" || return $?
|
|
done < <(find "${ARTIFACTS_PATH}/lib/" -type f -print0)
|
|
|
|
echo "Build initrd..."
|
|
cd "${root}" && find . -not -name '.keep' | cpio -L -v -o -H newc > "${output}" || return $?
|
|
}
|
|
|
|
# compile
|
|
function build() {
|
|
build_dropbear || return $?
|
|
build_initrd initrd || return $?
|
|
}
|
|
|
|
# run qemu
|
|
function run_qemu() {
|
|
local -r initrd="${ARTIFACTS_PATH}/${1:-initrd.cpio}"
|
|
local -r init="${2:-/init}"
|
|
shift 1 && shift 1
|
|
echo "Run qemu..."
|
|
qemu-system-aarch64 -nographic -m 64 -M virt -cpu cortex-a53 -kernel "${KERNEL_IMAGE}" -initrd "${initrd}" -append "console=ttyAMA0,115200 loglevel=9 earlyprintk init=${init} $*" -device "virtio-net-pci,netdev=net0" -netdev "user,id=net0,net=10.4.0.0/24,dhcpstart=10.4.0.100,hostfwd=tcp::${SSH_PORT}-:22" || return $?
|
|
}
|
|
|
|
# update ssh known_hosts
|
|
function update_known_hosts() {
|
|
echo "Update public key in known_hosts..."
|
|
mkdir -p "${HOME}/.ssh"
|
|
ssh-keygen -R "[${SSH_HOST}]:${SSH_PORT}"
|
|
echo "[${SSH_HOST}]:${SSH_PORT} $(cat "${ARTIFACTS_PATH}/dropbear_rsa_host_key.pub")" >> ~/.ssh/known_hosts
|
|
}
|
|
|
|
# ssh connect
|
|
function ssh_connect() {
|
|
local -r user="${1:-root}"
|
|
shift 1
|
|
echo "Connect with ssh to qemu..." >&2
|
|
# shellcheck disable=SC2029
|
|
ssh -p "${SSH_PORT}" -l "${user}" -i "${SSH_KEY}" "${SSH_HOST}" "$@" || return $?
|
|
}
|
|
|
|
# ssh cmd
|
|
function ssh_cmd() {
|
|
echo "Running '$*' over ssh..." >&2
|
|
ssh_connect root -n . /etc/profile\; "$@" || return $?
|
|
}
|
|
|
|
# scp copy
|
|
function scp_copy() {
|
|
local -r user=root
|
|
local -r dst="$1"
|
|
shift 1
|
|
echo "Copying '$*' over scp to '$dst'..."
|
|
scp -P "${SSH_PORT}" -i "${SSH_KEY}" "$@" "root@${SSH_HOST}:${dst}" || return $?
|
|
}
|
|
|
|
function find_modules() {
|
|
find "$MODULES_DIR" -mindepth 1 -maxdepth 1 -type d -not -name '_*' -print0
|
|
}
|
|
|
|
function modules_copy() {
|
|
echo "Copying modules and module tests..."
|
|
scp_copy "/lib/modules/$(ssh_cmd uname -r)/" "$MODULES_DST_DIR/"*.ko || return $?
|
|
scp_copy "/tmp/" "$MODULES_DST_DIR/"*.ko.test || return $?
|
|
}
|
|
function modules_load() {
|
|
echo "Load modules..."
|
|
ssh_cmd depmod || return $?
|
|
|
|
while IFS= read -r -d '' module_dir; do
|
|
ssh_cmd modprobe "$(basename "$module_dir")" || return 1
|
|
done < <(find_modules)
|
|
}
|
|
function modules_test() {
|
|
local module
|
|
echo "Test modules..."
|
|
while IFS= read -r -d '' module_dir; do
|
|
module="$(basename "$module_dir")"
|
|
echo "Running test for kernel module '$module'"
|
|
ssh_cmd "/tmp/${module}.ko.test" || return 1
|
|
done < <(find_modules)
|
|
}
|
|
function modules_unload() {
|
|
echo "Unload modules..."
|
|
|
|
while IFS= read -r -d '' module_dir; do
|
|
ssh_cmd rmmod -w "$(basename "$module_dir")" || return 1
|
|
done < <(find_modules)
|
|
}
|
|
|
|
# clean untracked git files
|
|
function clean() {
|
|
echo "Clean non git files..."
|
|
cd "${SCRIPT_DIR}" && git clean -dfx || return $?
|
|
}
|
|
|
|
function main() {
|
|
local -r cmd="${1:-all}"
|
|
shift 1
|
|
|
|
case "$cmd" in
|
|
all )
|
|
download_extract_all &&
|
|
compile &&
|
|
build ;;
|
|
|
|
download )
|
|
download_extract_all ;;
|
|
|
|
compile )
|
|
compile ;;
|
|
|
|
build )
|
|
build ;;
|
|
|
|
qemu )
|
|
run_qemu initrd.cpio /init "$@" ;;
|
|
|
|
ssh )
|
|
ssh_connect "$@" ;;
|
|
|
|
ssh_cmd )
|
|
ssh_cmd "$@" ;;
|
|
|
|
modules )
|
|
modules_compile &&
|
|
modules_copy &&
|
|
modules_load &&
|
|
modules_test &&
|
|
modules_unload ;;
|
|
|
|
modules_build )
|
|
modules_compile ;;
|
|
|
|
modules_copy )
|
|
modules_copy ;;
|
|
|
|
modules_load )
|
|
modules_load ;;
|
|
|
|
modules_test )
|
|
modules_test ;;
|
|
|
|
modules_unload )
|
|
modules_unload ;;
|
|
|
|
clean )
|
|
clean ;;
|
|
|
|
* )
|
|
echo "Error: unkown command" &&
|
|
false ;;
|
|
esac
|
|
|
|
return $?
|
|
}
|
|
|
|
main "$@"
|