Co-authored by M.Mościcki and Ł.Stelmach. Signed-off-by: Mateusz Mościcki <m.moscicki2@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Łukasz Stelmach <l.stelmach@xxxxxxxxxxx> --- tools/hsinit/Makefile.am | 29 ++++ tools/hsinit/README.org | 56 ++++++ tools/hsinit/bootstrap | 7 + tools/hsinit/configure.ac | 128 ++++++++++++++ tools/hsinit/hsinit.c | 299 +++++++++++++++++++++++++++++++++ tools/hsinit/vendor/.gitignore | 5 + tools/hsinit/vendor/SHA256SUMS | 2 + 7 files changed, 526 insertions(+) create mode 100644 tools/hsinit/Makefile.am create mode 100644 tools/hsinit/README.org create mode 100755 tools/hsinit/bootstrap create mode 100644 tools/hsinit/configure.ac create mode 100644 tools/hsinit/hsinit.c create mode 100644 tools/hsinit/vendor/.gitignore create mode 100644 tools/hsinit/vendor/SHA256SUMS diff --git tools/hsinit/Makefile.am tools/hsinit/Makefile.am new file mode 100644 index 000000000000..3c8b3cff1b64 --- /dev/null +++ tools/hsinit/Makefile.am @@ -0,0 +1,29 @@ +INITRAMFS_DIR = ./initramfs + +bin_PROGRAMS=hsinit +hsinit_LDFLAGS = $(ENABLE_STATIC) +hsinit_SOURCES = hsinit.c +hsinit_LINK = $(CCLD) $(hsinit_CFLAGS) $(CFLAGS) $(hsinit_LDFLAGS) \ + $(LDFLAGS) -o $@ + + +data_DATA = initramfs.cpio.xz + +CPIO = @CPIO@ +CPIO_FLAGS = -o --format=newc + +XZ=@XZ@ +XZ_FLAGS = --check=crc32 --lzma2=dict=1MiB -f -k + +ENABLE_STATIC = @ENABLE_STATIC@ + +%.xz : % + xz $(XZ_FLAGS) $< + +initramfs.cpio: hsinit + for d in bin dev etc lib mnt proc root sbin sys; do \ + mkdir -p $(INITRAMFS_DIR)/$$d; \ + done + cp $< $(INITRAMFS_DIR)/init + cd $(INITRAMFS_DIR); find . -print0 | cpio --null $(CPIO_FLAGS) > ../$@ + diff --git tools/hsinit/README.org tools/hsinit/README.org new file mode 100644 index 000000000000..2874781b5e70 --- /dev/null +++ tools/hsinit/README.org @@ -0,0 +1,56 @@ +* hsinit + + hsinit is a minimal init program for boot/loader (a.k.a. k-boot) on + Odroid XU4 and other platforms with limited size of bootloader + image. (1MiB on Odroid XU4). Its sole purpose is to unpack the rest + of the initramfs image from an archive pointed by the 'hs' parameter + in /proc/cmdline. For example: + +#+BEGIN_EXAMPLE: + hs=/dev/mmcblk1p6:uroot.cpio.gz +#+END_EXAMPLE + + will make hsinit mount /dev/mmcblk1p6 and unpack the content of + uroot.cpio.gz onto initramfs. Please note that unlike the old initrd + initramfs hasn't got fixed size and is much more suitable for hsinit + to work with. + +* Building hsinit + + The basic way to build hsinit on a host system is + +#+BEGIN_SRC sh + ./bootstrap && ./configure && make +#+END_SRC + + The only direct dependency of hsinit is [[https://libarchive.org/][libarchive]], which, however, + needs at least zlib to support reasonable compression. You use + libarchive and libz packaged for your system or download appropriate + archives into the vendor directory (see vendor/SHA256SUMS) and the + libraries shall be built automatically. + +* Building for musl + + While it is possible to build hsinit for the host system (e.g. x86) + and link it dynamically, it makes little sens. Most of the time you + will want to compile to for different CPU and link it statically against + a C library other than GNU C Library to make the binary small enough + to fit the image. + + To cross-compile hsinit for ARM and link it statically against musl + run the following command. + +#+BEGIN_SRC sh + ./bootstrap && \ + MUSL_DIR=/usr/lib/arm-linux-musleabi/ \ + GCC_CROSS_DIR=/usr/lib/gcc-cross/arm-linux-gnueabi/8/ \ + CPPFLAGS='-nostdinc -isystem /usr/include/arm-linux-musleabi/' \ + CFLAGS='-mthumb -Os -ffunction-sections -fdata-sections' \ + LIBARCHIVE_CPP_FLAGS=-I/usr/include/arm-linux-musleabi/ \ + LIBARCHIVE_C_FLAGS=$CFLAGS \ + ZLIB_C_FLAGS=$CFLAGS \ + LDFLAGS="-nostdlib -L${MUSL_DIR}/ -L${GCC_CROSS_DIR}/ ${MUSL_DIR}/crt1.o ${MUSL_DIR}/crti.o ${GCC_CROSS_DIR}/crtbegin.o -Wl,--gc-sections -Wl,--start-group ${GCC_CROSS_DIR}/libgcc.a ${GCC_CROSS_DIR}/libgcc_eh.a -Wl,--end-group ${GCC_CROSS_DIR}/crtend.o ${MUSL_DIR}/crtn.o -s" \ + LIBS="-lc -lgcc" + ./configure --enable-local-libraries --host=arm-linux-gnueabi --enable-static && \ + make +#+END_SRC diff --git tools/hsinit/bootstrap tools/hsinit/bootstrap new file mode 100755 index 000000000000..d3e9fbc8a6c3 --- /dev/null +++ tools/hsinit/bootstrap @@ -0,0 +1,7 @@ +#!/bin/sh + +set -x -e +#aclocal +#autoheader +#autoconf +autoreconf -i -f -v diff --git tools/hsinit/configure.ac tools/hsinit/configure.ac new file mode 100644 index 000000000000..135f53354c9a --- /dev/null +++ tools/hsinit/configure.ac @@ -0,0 +1,128 @@ +dnl +dnl configure.ac for hsinit +dnl + +AC_PREREQ(2.69) +AC_INIT(hsinit, 1.0.0) +AC_CONFIG_SRCDIR([./hsinit.c]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([config.h]) +AC_LANG(C) +AC_PROG_CC +PKG_PROG_PKG_CONFIG + +dnl -- Prepare for cross-compilation +AC_CANONICAL_HOST +if test "${build}" != "${host}" ; then + AC_CHECK_PROGS(BUILD_CC, [${build_alias}-gcc ${build}-gcc gcc]) + AC_CHECK_PROGS(BUILD_LD, [${build_alias}-ld ${build}-ld ld]) + AC_CHECK_PROGS(HOST_CC, [${host_alias}-gcc ${host}-gcc gcc]) + AC_CHECK_PROGS(HOST_LD, [${host_alias}-ld ${host}-ld ld]) +else + BUILD_CC="$CC" + BUILD_LD="$LD" + + HOST_CC="$CC" + HOST_LD="$LD" +fi + + +AC_ARG_ENABLE(local-libraries, + AS_HELP_STRING([--enable-local-libraries],[Build and use local versions of libarchive and zlib]), + ENABLE_LOCAL_LIBS=yes) + +AC_ARG_ENABLE(static, + AS_HELP_STRING([--enable-static], [Build static binary]), + ENABLE_STATIC=-static) + +AC_ARG_VAR(ZLIB_C_FLAGS, [CFLAGS for local zlib compilation]) +AC_ARG_VAR(ZLIB_CPP_FLAGS, [CPPFLAGS for local zlib compilation]) +AC_ARG_VAR(ZLIB_LD_FLAGS, [LDFLAGS for local zlib compilation]) +AC_ARG_VAR(LIBARCHIVE_C_FLAGS, [CFLAGS for local libarchive compilation]) +AC_ARG_VAR(LIBARCHIVE_CPP_FLAGS, [CPPFLAGS for local libarchive compilation]) +AC_ARG_VAR(LIBARCHIVE_LD_FLAGS, [LDFLAGS for local libarchive compilation]) + +AC_SUBST(ENABLE_STATIC) +AC_SUBST(HOST_CFLAGS, ["$CFLAGS"]) +AC_SUBST(HOST_CPPFLAGS, ["$CPPFLAGS"]) +AC_SUBST(HOST_LDFLAGS, ["$LDFLAGS"]) + +AC_PATH_PROG(CPIO, cpio) +AC_PATH_PROG(XZ, xz) +AC_PATH_PROG(SHA256SUM, sha256sum) + + +AS_IF([test -n "${ENABLE_LOCAL_LIBS}"], + AC_MSG_CHECKING([for local libraries]) + AS_IF([cd $srcdir/vendor && $SHA256SUM -c SHA256SUMS --quiet && cd - > /dev/null], + AC_MSG_RESULT([ok]), + AC_MSG_ERROR([error]))) +AS_IF([test -n "${ENABLE_LOCAL_LIBS}" && /bin/true], + pushd vendor + mkdir -p libs + zlib_archive=$(grep -o zlib-.* SHA256SUMS) + zlib_dir=${zlib_archive%.tar.gz} + AC_MSG_NOTICE([Building ${zlib_dir}]) + rm -rf "$zlib_dir" + tar -xf "$zlib_archive" + cd "$zlib_dir" + CFLAGS="${ZLIB_C_FLAGS}" \ + CPPFLAGS="${ZLIB_CPP_FLAGS}" \ + LDFLAGS="${ZLIB_LD_FLAGS}" \ + CROSS_PREFIX="${host_alias:-${host}}-" ./configure ${ENABLE_STATIC:+--static} --prefix=$(pwd)/../libs + make -j8 install + + cd .. + + libarchive_archive=$(grep -o libarchive-.* SHA256SUMS) + libarchive_dir=${libarchive_archive%.tar.gz} + rm -rf "$libarchive_dir" + tar -xf "$libarchive_archive" + cd $libarchive_dir + + PKG_CONFIG_PATH=$(pwd)/../libs/lib/pkgconfig + CFLAGS="${LIBARCHIVE_C_FLAGS}" \ + CPPFLAGS="${LIBARCHIVE_CPP_FLAGS}" \ + LDFLAGS="${LIBARCHIVE_LD_FLAGS}" \ + ./configure --host=${host_alias:-${host}} \ + ${ENABLE_STATIC:+--enable-static --disable-shared} \ + --prefix=$(pwd)/../libs \ + --disable-bsdtar \ + --disable-bsdcat \ + --disable-bsdcpio \ + --disable-xattr \ + --disable-acl \ + --disable-shared \ + --disable-largefile \ + --without-bz2lib \ + --without-iconv \ + --without-lz4 \ + --without-lzma \ + --without-lzo2 \ + --without-cng \ + --without-nettle \ + --without-openssl \ + --without-xml2 \ + --without-expat + make -j8 install + popd +) + +AS_IF([test -n "${ENABLE_LOCAL_LIBS}"], + export PKG_CONFIG_PATH=$(pwd)/vendor/libs/lib/pkgconfig/ +) +AS_IF([test -n "$ENABLE_STATIC"], + [PKG_CHECK_MODULES_STATIC([LIBARCHIVE], [libarchive])], + [PKG_CHECK_MODULES([LIBARCHIVE], [libarchive])]) + +CFLAGS="$CFLAGS $LIBARCHIVE_CFLAGS" +LIBS="$LIBS $LIBARCHIVE_LIBS" + +dnl ---Output +AC_OUTPUT([Makefile]) + +echo +echo Host CC: $HOST_CC +echo Build CC: $BUILD_CC +echo diff --git tools/hsinit/hsinit.c tools/hsinit/hsinit.c new file mode 100644 index 000000000000..8332c06b8501 --- /dev/null +++ tools/hsinit/hsinit.c @@ -0,0 +1,299 @@ +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <stdbool.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <archive.h> +#include <archive_entry.h> +#include <dirent.h> + + +#define COMMAND_LINE_SIZE 512 +#define PARAM_NAME "hs" + +/* + * Based on examples: + * https://github.com/libarchive/libarchive/wiki/Examples + */ +int copy_data(struct archive *ar, struct archive *aw) +{ + int r; + const void *buff; + size_t size; + int64_t offset; + + for (;;) { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return (ARCHIVE_OK); + if (r < ARCHIVE_OK) + return (r); + r = archive_write_data_block(aw, buff, size, offset); + if (r < ARCHIVE_OK) { + printf("%s\n", archive_error_string(aw)); + return (r); + } + } +} + +static int extract(const char *filename) +{ + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + int r; + + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + a = archive_read_new(); + archive_read_support_format_cpio(a); + archive_read_support_filter_gzip(a); + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + + if ((r = archive_read_open_filename(a, filename, 10240))) + return -1; + + for (;;) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + break; + if (r < ARCHIVE_OK) + printf("%s\n", archive_error_string(a)); + if (r < ARCHIVE_WARN) + return -1; + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + printf("%s\n", archive_error_string(ext)); + else if (archive_entry_size(entry) > 0) { + r = copy_data(a, ext); + if (r < ARCHIVE_OK) + printf("%s\n", archive_error_string(ext)); + if (r < ARCHIVE_WARN) + return -1; + } + + r = archive_write_finish_entry(ext); + + if (r < ARCHIVE_OK) + printf("%s\n", archive_error_string(ext)); + if (r < ARCHIVE_WARN) + return -1; + } + + archive_read_close(a); + archive_read_free(a); + archive_write_close(ext); + archive_write_free(ext); + + return 1; +} + +int mount_sysfs() +{ + return mount("none", "/sys", "sysfs", MS_NOEXEC | + MS_RELATIME | MS_NODEV | MS_NOSUID, NULL); +} + +int mount_procfs() +{ + return mount("none", "/proc", "proc", MS_NOEXEC | + MS_RELATIME | MS_NODEV | MS_NOSUID, NULL); +} + +int mount_devfs() +{ + return mount("none", "/dev", "devtmpfs", MS_NOEXEC | + MS_RELATIME | MS_NOSUID, NULL); +} + +int mount_storage(char *device) +{ + char BUFF[256]; + sprintf(BUFF, "%s", device); + // mount("/dev/sdb6", "/mnt", "vfat", MS_RDONLY, NULL) = 0 + return mount(BUFF, "/mnt", "vfat", 0, NULL); +} + +void ls(char *dir) +{ + DIR *d = NULL; + struct dirent *de; + + d = opendir(dir); + + if (d == NULL) { + printf("Error opening directory: %s\n", dir); + return; + } + + printf("Reading %s\n", dir); + while ((de = readdir(d)) != NULL) { + printf("%s\n", de->d_name); + } + printf("\n"); +} + +int umount_storage() +{ + return umount("/mnt"); +} + +int read_cmd_params(char *buff) +{ + int fd = open("/proc/cmdline", O_RDONLY); + if (fd < 0) { + printf("Open /proc/cmdline error: %s", strerror(errno)); + return fd; + } + + int readed = read(fd, buff, COMMAND_LINE_SIZE-1); + + if (readed >= 0) + buff[readed] = '\0'; + return readed; +} + +/* + * Get param=value from /proc/cmdline + */ +const char *find_param(const char *cmdline, const char *param_name, char *value) +{ + const char *cur = cmdline; + bool in_quote = false; + bool begin = true; + int param_name_len = strlen(param_name); + int i, length; + + while (*cur != '\0') { + if (begin) { + for (i = 0; *(param_name+i) != '\0' && + *(cur + i) == *(param_name+i); i++); + + if (i == param_name_len && (*(cur+i) == '=')) { + cur += i+1; + break; + } + begin = false; + } + + if (*cur == '\"') + in_quote = !in_quote; + + if (!in_quote && *cur == ' ') + begin = true; + + cur++; + } + + if (*cur > 0) { + for (i = 0; *(cur+i) != ' ' && *(cur+i) != '\0' && *(cur+i) != '\n'; i++); + length = i > (COMMAND_LINE_SIZE - 1) ? (COMMAND_LINE_SIZE - 1) : i; + memcpy(value, cur, length); + value[length] = '\0'; + } + return cur; +} + +int parse_cmdline(const char *cmdline, char *device, char *dest) +{ + char value[COMMAND_LINE_SIZE]; + const char *res = find_param(cmdline, PARAM_NAME, value); + + if (*res == 0) { + printf("hs param not found\n"); + return 0; + } + + char *r = strtok(value, ":"); + if (r == NULL) { + printf("Wrong sh parameter format\n"); + return -1; + } + + strncpy(device, r, NAME_MAX); + + r = strtok(NULL, ":"); + if (r == NULL) { + printf("Wrong hs parameter format\n"); + return -1; + } + + strncpy(dest, r, NAME_MAX); + + return 1; +} + +int main(void) +{ + char params[COMMAND_LINE_SIZE]; + char device[NAME_MAX]; + char initramfs_fn[NAME_MAX]; + char full_irfs_file[PATH_MAX]; + + printf("Hello...\n"); + + if (mount_sysfs() < 0) { + printf("Mount sysfs error: %s\n", strerror(errno)); + exit(1); + } + + if (mount_procfs() < 0) { + printf("Mount procfs error: %s\n", strerror(errno)); + exit(1); + } + + if (mount_devfs() < 0) { + printf("Mount devfs error: %s\n", strerror(errno)); + exit(1); + } + + printf("Sleep zzZZ...\n"); + sleep(1); + printf("Wake up :)\n"); + + ls("/dev"); + + if (read_cmd_params(params) < 0) { + printf("Read cmd_params error: %s\n", strerror(errno)); + exit(1); + } + + if (parse_cmdline(params, device, initramfs_fn) <= 0) + exit(1); + + if (mount_storage(device) < 0) { + printf("Mount %s error: %s\n", device, strerror(errno)); + exit(1); + } + + sprintf(full_irfs_file, "/mnt/%s", initramfs_fn); + + if (extract(full_irfs_file) <= 0) { + printf("Extract error: %s\n", strerror(errno)); + exit(1); + } + + umount_storage(); + + /*if (execl("/bin/busybox", "sh", NULL) <= 0)*/ + /*printf("Execl error: %s\n", strerror(errno));*/ + + printf("Init...\n"); + + if (execl("/init", "/init", (char *)NULL) <= 0) + printf("Execl error: %s\n", strerror(errno)); + + return 0; +} diff --git tools/hsinit/vendor/.gitignore tools/hsinit/vendor/.gitignore new file mode 100644 index 000000000000..07a4d737cb87 --- /dev/null +++ tools/hsinit/vendor/.gitignore @@ -0,0 +1,5 @@ +/zlib-*/ +/libarchive-*/ +/libs/ +/libarchive-*.tar.gz +/zlib-*.tar.gz diff --git tools/hsinit/vendor/SHA256SUMS tools/hsinit/vendor/SHA256SUMS new file mode 100644 index 000000000000..fd42c44ba948 --- /dev/null +++ tools/hsinit/vendor/SHA256SUMS @@ -0,0 +1,2 @@ +c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 zlib-1.2.11.tar.gz +ed2dbd6954792b2c054ccf8ec4b330a54b85904a80cef477a1c74643ddafa0ce libarchive-3.3.2.tar.gz -- 2.20.1