[RFC PATCH 3/4] Add tools/hsinit

From: Åukasz Stelmach
Date: Thu Jan 30 2020 - 07:43:23 EST


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