commit 1c754e754df94757a0d5299a87c923e2f3cb552c Author: Hangfan Li Date: Fri Aug 22 16:51:27 2025 +0800 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6f455f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build +*.kate-swp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..89af2b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +#!/usr/bin/env make + +##### Have default "all" target listed at the beginning +.PHONY: default +default: all + +define stack_include +$$(info Makefile "$$(lastword $$(MAKEFILE_STACK))" includes "$1") +MAKEFILE_STACK += $1 +CURRENT_MAKEFILE := $$(lastword $$(MAKEFILE_STACK)) +CURRENT_DIR := $$(dir $$(CURRENT_MAKEFILE)) +include $1 +MAKEFILE_STACK := $$(wordlist 1,$$(shell expr $$(words $$(MAKEFILE_STACK)) - 1),$$(MAKEFILE_STACK)) +CURRENT_MAKEFILE := $$(lastword $$(MAKEFILE_STACK)) +CURRENT_DIR := $$(dir $$(CURRENT_MAKEFILE)) +endef +MAKEFILE_STACK := $(dir .)$(lastword $(MAKEFILE_LIST)) +CURRENT_MAKEFILE := $(lastword $(MAKEFILE_STACK)) +CURRENT_DIR := $(dir $(CURRENT_MAKEFILE)) + +##### Setup out-of-tree build +# ROOT_DIR & WORK_DIR are all absolute path. +# As well as DIST_DIR & STAGING_DIR +ROOT_DIR := $(realpath .) +# O= must be an absolute path, otherwise unexpected behavior will occur when used in combination with make -C +ifdef O +WORK_DIR := $(realpath $(O)) +else +WORK_DIR := $(ROOT_DIR)/build +endif +STAGING_DIR := $(WORK_DIR)/staging +DIST_DIR := $(WORK_DIR)/dist + +# Collected targets +ROOTFS_TARGETS := +BSP_TARGETS := + +# Makefile function definations +$(eval $(call stack_include,$(CURRENT_DIR)functions.mk)) + +# Load receipes +$(eval $(call stack_include,$(CURRENT_DIR)receipes/receipes.mk)) + +.PHONY: all_rootfs +all_rootfs: $(ROOTFS_TARGETS) + +.PHONY: clean +clean: + rm -rf "$(WORK_DIR)" + +.PHONY: .test +.test: + @echo $(MAKEFILE_STACK) diff --git a/functions.mk b/functions.mk new file mode 100644 index 0000000..f236880 --- /dev/null +++ b/functions.mk @@ -0,0 +1,111 @@ +######## +dir_guard=@mkdir -p $(@D) + +######## +define distro_begin +ifdef DISTRO_NAME +$$(error Distro section "$$(DISTRO_NAME)" is not finished yet) +endif +DISTRO_NAME := $(1) +endef + +######## +define distro_end +ifndef DISTRO_NAME +$$(error No paired distro_begin found) +endif +ifdef RELEASE_NAME +$$(error Unfinished distro release "$$(DISTRO_NAME) $$(RELEASE_NAME)" found) +endif +undefine DISTRO_NAME +endef + +######## +define distro_release_begin +ifndef DISTRO_NAME +$$(error Not in distro section) +endif +ifdef RELEASE_NAME +$$(error Distro release "$$(DISTRO_NAME) $$(RELEASE_NAME)" is not finished yet) +endif +RELEASE_NAME := $(1) +endef + +######## +define distro_release_end +ifndef RELEASE_NAME +$$(error No paired distro_release_begin found) +endif +undefine RELEASE_NAME +endef + +######## +define add_receipe + +ifndef RELEASE_NAME +$$(error Not within distro release section) +endif + +# Cleanup and register new receipe +undefine RECEIPE_NAME +undefine ROOTFS_COMPRESSION +undefine EXTENDS +include $$(CURRENT_DIR)$1/receipe.mk + +# Check name +ifndef RECEIPE_NAME +$$(error RECEIPE_NAME not defined) +endif +# Check compression method +ifeq ($$(ROOTFS_COMPRESSION),zstd) +ROOTFS_COMPRESSION_SUFFIX := tar.zst +ROOTFS_COMPRESSION_CMD := tar --use-compress-program=zstd -cf "$$$$$$$${OUTPUT_ARCHIVE}" -C "$$$$$$$${ROOTFS_DIR}" . +else +$$(error Unsupported compression mode) +endif + +# Define global references +$$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_DIR := $$(CURRENT_DIR)$1 +$$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT := $(DIST_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/$$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs.$$(ROOTFS_COMPRESSION_SUFFIX) +ROOTFS_TARGETS += $$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs + +# Populate workdir +# Directory timestamp is not reliable so always resync staging dir +.PHONY: $(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME) +$(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME): $$(foreach depname,$$(EXTENDS),$$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(depname)_DIR)/) $$(CURRENT_DIR)$1/ + $$(dir_guard) + rm -rf "$$@" + @# Not preserving modification time, aka -t + rsync -rlpgoD --exclude receipe.mk $$+ $$@ + +# Define entry target +.PHONY: $$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs +$$(DISTRO_NAME)-$$(RELEASE_NAME)-$$(RECEIPE_NAME)-rootfs: $$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT) +define GENERATE_TAR_TARGET +$$($$(DISTRO_NAME)_$$(RELEASE_NAME)_$$(RECEIPE_NAME)_ARTIFACT): $(STAGING_DIR)/$$(DISTRO_NAME)/$$(RELEASE_NAME)/receipes/$$(RECEIPE_NAME) + $$$$(dir_guard) + docker run --rm \ + -v $(ROOT_DIR)/tools:/image-builder/tools:ro \ + -v $$$$+:/image-builder/receipe:ro \ + -v $$$$(dir $$$$@):/image-builder/output \ + -it docker.io/openeuler/openeuler:24.03-lts \ + /bin/sh -c ' \ + mkdir /tmp/rootfs && \ + cd /image-builder/tools && \ + ./build_rootfs.sh -i /image-builder/receipe -o /tmp/rootfs && \ + export OUTPUT_ARCHIVE=/image-builder/output/$$$$(notdir $$$$@) && \ + export ROOTFS_DIR=/tmp/rootfs && \ + $$(ROOTFS_COMPRESSION_CMD)' +endef +$$(eval $$(GENERATE_TAR_TARGET)) + + +# Reset registers again +undefine GENERATE_TAR_TARGET +undefine EXTENDS +undefine ROOTFS_COMPRESSION_CMD +undefine ROOTFS_COMPRESSION_SUFFIX +undefine ROOTFS_COMPRESSION +undefine RECEIPE_NAME + +endef diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/board.mk b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/board.mk new file mode 100644 index 0000000..e69de29 diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/image-layout.conf b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/image-layout.conf new file mode 100644 index 0000000..8a040f1 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/image-layout.conf @@ -0,0 +1,23 @@ +DISK_TABLE_TYPE=GUID + +PARTITION_1_OFFSET=1MB +PARTITION_1_LENGTH=499MB +PARTITION_1_NAME="oerv-boot" +PARTITION_1_UUID= +PARTITION_1_TYPE=bls_boot +PARTITION_1_ATTRS=legacy_boot +PARTITION_1_USAGE=MOUNTPOINT +PARTITION_1_MOUNTPONT="/boot" +PARTITION_1_FILESYSTEM=vfat +PARTITION_1_FS_UUID= + +PARTITION_2_OFFSET= +PARTITION_2_LENGTH= +PARTITION_2_NAME="oerv-root" +PARTITION_2_UUID= +PARTITION_2_TYPE=linux_root_riscv64 +PARTITION_2_ATTRS= +PARTITION_2_USAGE=MOUNTPOINT +PARTITION_2_MOUNTPONT="/" +PARTITION_1_FILESYSTEM=ext4 +PARTITION_1_FS_UUID= diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/packages.list b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/packages.list new file mode 100644 index 0000000..16475d0 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/packages.list @@ -0,0 +1,3 @@ +firmware-spacemit-k1 +dracut +u-boot-menu diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-dracut/50-spacemit_k1_fw.conf b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-dracut/50-spacemit_k1_fw.conf new file mode 100644 index 0000000..e3b8e4c --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-dracut/50-spacemit_k1_fw.conf @@ -0,0 +1,3 @@ +# Provide esos.elf for early kernel direct filesystem lookup + +install_items+= /lib/firmware/esos.elf diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-dracut/entry.sh b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-dracut/entry.sh new file mode 100644 index 0000000..e69de29 diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-u-boot-menu/entry.sh b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-u-boot-menu/entry.sh new file mode 100644 index 0000000..e69de29 diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-u-boot-menu/u-boot b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-u-boot-menu/u-boot new file mode 100644 index 0000000..47e7670 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/40-kenel-preq/post.d/50-u-boot-menu/u-boot @@ -0,0 +1,6 @@ +U_BOOT_PROMPT="2" +U_BOOT_MENU_LABEL="OpenEuler GNU/Linux, kernel" +# tty enabled on both serial0 and fbdev +U_BOOT_PARAMETERS="rootwait rw earlycon=sbi console=tty0 console=ttyS0,115200 rd.multipath=0" +U_BOOT_ROOT= +U_BOOT_FDT_DIR="/dtb-" diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/60-kernel/packages.list b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/60-kernel/packages.list new file mode 100644 index 0000000..f1b33e1 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/packages.d/60-kernel/packages.list @@ -0,0 +1,2 @@ +kernel +kernel-headers diff --git a/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/repos.d/bsp.repo b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/repos.d/bsp.repo new file mode 100644 index 0000000..c13bb7a --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/bsp/spacemit-k1/bootstd-extlinux/repos.d/bsp.repo @@ -0,0 +1,13 @@ +[bsp-common] +name=BSP-Common +type=rpm-md +baseurl=https://build-repo.tarsier-infra.isrc.ac.cn//home:/salimterryli:/bsp:/common/openeuler-24.03-sp2/ +enabled=1 +gpgcheck=0 + +[bsp-spacemit-k1] +name=BSP-K1 +type=rpm-md +baseurl=https://build-repo.tarsier-infra.isrc.ac.cn//home:/salimterryli:/bsp:/spacemit:/k1/openeuler-24.03-sp2/ +enabled=1 +gpgcheck=0 diff --git a/receipes/openeuler/24.03-lts-sp2/release.mk b/receipes/openeuler/24.03-lts-sp2/release.mk new file mode 100644 index 0000000..b7e4d37 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/release.mk @@ -0,0 +1,8 @@ +$(eval $(call distro_release_begin,24.03-LTS-SP2)) + +$(eval $(call add_receipe,userspace/base)) +$(eval $(call add_receipe,userspace/xfce4)) + +$(foreach mk,$(wildcard $(CURRENT_DIR)bsp/*/board.mk),$(eval $(call stack_include,$(mk)))) + +$(eval $(call distro_release_end)) diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/packages.list b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/packages.list new file mode 100644 index 0000000..3766ff8 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/packages.list @@ -0,0 +1 @@ +openEuler-repos diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/post.d/50-remove-bootstrap-repo/entry.sh b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/post.d/50-remove-bootstrap-repo/entry.sh new file mode 100755 index 0000000..f1f7d06 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/00-setup-repo/post.d/50-remove-bootstrap-repo/entry.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env dash + +rm "${ROOTFS_DIR}/etc/yum.repos.d/bootstrap.repo" diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/50-base-group/packages.group b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/50-base-group/packages.group new file mode 100644 index 0000000..df967b9 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/base/packages.d/50-base-group/packages.group @@ -0,0 +1 @@ +base diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/base/receipe.mk b/receipes/openeuler/24.03-lts-sp2/userspace/base/receipe.mk new file mode 100644 index 0000000..ea28953 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/base/receipe.mk @@ -0,0 +1,2 @@ +RECEIPE_NAME := base +ROOTFS_COMPRESSION := zstd diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/base/repos.d/bootstrap.repo b/receipes/openeuler/24.03-lts-sp2/userspace/base/repos.d/bootstrap.repo new file mode 100644 index 0000000..04d1dc4 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/base/repos.d/bootstrap.repo @@ -0,0 +1,10 @@ +# Temporary repo for openeuler-repos package installation + +[OS] +name=OS +baseurl=https://mirror.isrc.ac.cn/openeuler/openEuler-24.03-LTS-SP1/OS/$basearch/ +# metalink=https://mirrors.openeuler.org/metalink?repo=$releasever/OS&arch=$basearch +metadata_expire=1h +enabled=1 +gpgcheck=1 +gpgkey=http://mirror.isrc.ac.cn/openeuler/openEuler-24.03-LTS-SP1/OS/$basearch/RPM-GPG-KEY-openEuler diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/packages.d/60-xfce4.list b/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/packages.d/60-xfce4.list new file mode 100644 index 0000000..e3ca179 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/packages.d/60-xfce4.list @@ -0,0 +1,8 @@ +xorg-* +xfwm4 +xfdesktop +xfce4-* +xfce4-*-plugin +lightdm +lightdm-gtk +linux-firmware diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/receipe.mk b/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/receipe.mk new file mode 100644 index 0000000..3b72960 --- /dev/null +++ b/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/receipe.mk @@ -0,0 +1,3 @@ +RECEIPE_NAME := xfce4 +ROOTFS_COMPRESSION := zstd +EXTENDS := base diff --git a/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/repos.d/Desktop.repo b/receipes/openeuler/24.03-lts-sp2/userspace/xfce4/repos.d/Desktop.repo new file mode 100644 index 0000000..e69de29 diff --git a/receipes/openeuler/distro.mk b/receipes/openeuler/distro.mk new file mode 100644 index 0000000..349cd2a --- /dev/null +++ b/receipes/openeuler/distro.mk @@ -0,0 +1,7 @@ +$(eval $(call distro_begin,openEuler)) + +PACKAGE_MANAGER := yum + +$(foreach mk,$(wildcard $(CURRENT_DIR)*/release.mk),$(eval $(call stack_include,$(mk)))) + +$(eval $(call distro_end)) diff --git a/receipes/receipes.mk b/receipes/receipes.mk new file mode 100644 index 0000000..059318b --- /dev/null +++ b/receipes/receipes.mk @@ -0,0 +1 @@ +$(foreach mk,$(wildcard $(CURRENT_DIR)*/distro.mk),$(eval $(call stack_include,$(mk)))) diff --git a/tools/build_rootfs.sh b/tools/build_rootfs.sh new file mode 100755 index 0000000..304f235 --- /dev/null +++ b/tools/build_rootfs.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env sh + +set -o errexit +set -o nounset + +. ./logging.sh + +RECEIPE_DIR="" +ROOTFS_DIR="" + + +print_usage() { + # Keep help message within 80 cols for special consoles. + cat << EOF + +Usage: $0 [-hp] [-i input [-i input [...]]] output + -h Print this help message. + -i receipe Path to directory containing the receipe + to make the rootfs. + -o rootfs Path to directory that the rootfs will + be bootstrapped into. +Environment Variables: + LOG_LEVEL Controls the log level of this script. + One of: DEBUG, INFO, WARN, ERROR + Defaults to: INFO +EOF +} + +main() { + # Handling args + while getopts "i:o:h" o; do + case "${o}" in + h) + print_usage + exit 0 + ;; + i) + RECEIPE_DIR=${OPTARG} + ;; + o) + ROOTFS_DIR=${OPTARG} + ;; + *) + exit 1 + ;; + esac + done + shift $((OPTIND-1)) + + # Validate inputs + if [ -z "${RECEIPE_DIR}" ]; then + loge "No receipe is specified!" + exit 1 + fi + if [ -z "${ROOTFS_DIR}" ]; then + loge "No output dir is specified!" + exit 1 + fi + + logi "Using receipe: ${RECEIPE_DIR}" + logi "ROOTFS_DIR=${ROOTFS_DIR}" + + exit 0 +} + +main "$@" diff --git a/tools/gptcfg.sh b/tools/gptcfg.sh new file mode 100755 index 0000000..aa39ec9 --- /dev/null +++ b/tools/gptcfg.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env dash +# shellcheck shell=dash disable=SC2034 + +set -o errexit +set -o nounset + +. ./logging.sh + +TARGET_FILE="" + +################################################### + +readonly TYPENAME=" +efi +bls_boot +linux_data +linux_root_riscv64 +linux_swap +msfdata +" + +readonly TYPECODE_EFI="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" +readonly TYPECODE_BLS_BOOT="BC13C2FF-59E6-4262-A352-B275FD6F7172" +readonly TYPECODE_LINUX_DATA="0FC63DAF-8483-4772-8E79-3D69D8477DE4" +readonly TYPECODE_LINUX_ROOT_RISCV64="72EC70A6-CF74-40E6-BD49-4BDA08E8F224" +readonly TYPECODE_LINUX_SWAP="0657fd6d-a4ab-43c4-84e5-0933c84b4f4f" +readonly TYPECODE_MSFDATA="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" +readonly TYPECODE_MSFRESV="e3c9e316-0b5c-4db8-817d-f92df00215ae" + +readonly TYPECODE_COMMON_ATTRS_LEGACY_BOOT=2 +readonly TYPECODE_LINUX_ATTRS_NO_AUTO=63 +readonly TYPECODE_LINUX_ATTRS_RO=60 +readonly TYPECODE_LINUX_ATTRS_GROWFS=59 +readonly TYPECODE_MSFDATA_ATTRS_NO_AUTO=63 +readonly TYPECODE_MSFDATA_ATTRS_RO=60 +readonly TYPECODE_MSFDATA_HIDDEN=62 + +#readonly TYPECODE_EFI_ATTRS="" + +readonly TYPECODE_BLS_BOOT_ATTRS=" +legacy_boot +no_auto +ro +growfs +" +readonly TYPECODE_BLS_BOOT_ATTRS_LEGACY_BOOT=$TYPECODE_COMMON_ATTRS_LEGACY_BOOT +readonly TYPECODE_BLS_BOOT_ATTRS_NO_AUTO=$TYPECODE_LINUX_ATTRS_NO_AUTO +readonly TYPECODE_BLS_BOOT_ATTRS_RO=$TYPECODE_LINUX_ATTRS_RO +readonly TYPECODE_BLS_BOOT_ATTRS_GROWFS=$TYPECODE_LINUX_ATTRS_GROWFS + +#readonly TYPECODE_LINUX_DATA_ATTRS="" + +readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS=" +legacy_boot +no_auto +ro +growfs +" +readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_LEGACY_BOOT=$TYPECODE_COMMON_ATTRS_LEGACY_BOOT +readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_NO_AUTO=$TYPECODE_LINUX_ATTRS_NO_AUTO +readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_RO=$TYPECODE_LINUX_ATTRS_RO +readonly TYPECODE_LINUX_ROOT_RISCV64_ATTRS_GROWFS=$TYPECODE_LINUX_ATTRS_GROWFS + +#readonly TYPECODE_LINUX_SWAP_ATTRS="" + +################################################### + +str_to_upper() { + printf '%s' "$1" | tr '[:lower:]' '[:upper:]' +} + +getvarbyname() { + eval printf '%s' \"\$\{"$1"\}\" +} + +handle_set_uuid() { + if [ $# -eq 0 ]; then + loge "part_id not specified." + exit 2 + fi + local partid; partid="$1"; shift 1 + local new_uuid; new_uuid="${1:-}" + if [ -z "${new_uuid}" ]; then + new_uuid="$(uuidgen)" + fi + logd "Setting new UUID to part ${partid}: ${new_uuid}" + 1>/dev/null sgdisk -u "${partid}:${new_uuid}" "${TARGET_FILE}" +} + +handle_set_type() { + if [ $# -eq 0 ]; then + loge "part_id not specified." + exit 2 + fi + local partid; partid="$1"; shift 1 + if [ $# -eq 0 ]; then + loge "Type not specified." + exit 2 + fi + local parttype; parttype="$1"; shift 1 + + if ! printf '%s' "${TYPENAME}" | grep -E "^${parttype}$" > /dev/null; then + loge "Unsupported type: ${parttype}" + exit 2 + fi + + local typecode + typecode="$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")")" + + local supported_attrs="" + supported_attrs="$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")_ATTRS:-")" + local typeattrbits="" + while [ $# -ne 0 ]; do + local curr_attr="$1" + shift 1 + if ! printf '%s' "${supported_attrs}" | grep -E "^${curr_attr}$" > /dev/null; then + loge "Usupported attribute for type ${parttype}: ${curr_attr}" + exit 2 + fi + typeattrbits="${typeattrbits}$(getvarbyname "TYPECODE_$(str_to_upper "${parttype}")_ATTRS_$(str_to_upper "${curr_attr}")") +" + done + logd "Set type code to ${typecode} for part ${partid}" + 1>/dev/null sgdisk -t "${partid}:${typecode}" "${TARGET_FILE}" + printf '%s' "${typeattrbits}" | while read -r attrbit ; do + if [ -z "${attrbit}" ]; then + continue + fi + logd "Set partition attribute bit ${attrbit} for part ${partid}" + 1>/dev/null sgdisk -A "${partid}:set:${attrbit}" "${TARGET_FILE}" + done +} + +handle_set_name() { + if [ $# -eq 0 ]; then + loge "part_id not specified." + exit 2 + fi + local partid; partid="$1"; shift 1 + + if [ $# -eq 0 ]; then + loge "part name not specified." + exit 2 + fi + local partname; partname="$1"; shift 1 + 1>/dev/null sgdisk -c "${partid}:${partname}" "${TARGET_FILE}" +} + +print_usage() { + # Keep help message within 80 cols for special consoles. + cat << EOF +Configure GUID partition attributes +Usage: $0 [-h] target command [parameters] + target Path to the disk image file. Can also be a real blockdev. +Commands: + set-name part_id name + Set the GUID partition name. + Not to be confused with filesystem label! + part_id Partition index, starting from 1. + name Name of the partition. Maximum 71 bytes. + set-type part_id type [attrs [attrs [...]]] + Configure the partition into a pre-defined type. + On GUID disks, only one PartitionTypeGUID can be set but + multiple Attributes can be applied to the same partition. + The supported types are picked from BLS and DPS. + Each TypeGUID may support different attrs. + part_id Partition index, starting from 1. + type Partition usage type. Available options: + - efi + A standard EFI System Partition. Note that the filesystem + still need to be configured by other tools. + This type will set: + - PartitionTypeGUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B + - bls_boot + A Boot Loader Specification compatible partition. + Also known as XBOOTLDR. May be auto-mounted by systemd. + This type will set: + - PartitionTypeGUID: BC13C2FF-59E6-4262-A352-B275FD6F7172 + Supported attributes: + - legacy_boot + - no_auto: Do not mount automatically. + - ro: Mount as read-only. + - growfs: Resize fs to its container size on mounted. + - linux_data + A normal partition that may hold any of filesystem + supported by Linux. No special function. + This type will set: + - PartitionTypeGUID: 0fc63daf-8483-4772-8e79-3d69d8477de4 + - linux_root_riscv64 + A root partition containing riscv64 rootfs. May be auto + -mounted by systemd. + This type will set: + - PartitionTypeGUID: 72ec70a6-cf74-40e6-bd49-4bda08e8f224 + Supported attributes: + - legacy_boot + - no_auto: Do not mount automatically. + - ro: Mount as read-only. + - growfs: Resize fs to its container size on mounted. + - linux_swap + A swap partition. May be automatically enabled by systemd + This type will set: + - PartitionTypeGUID: 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f + set-uuid part_id [uuid] + Set the UniquePartitionGUID. + part_id Partition index, starting from 1. + uuid UniquePartitionGUID, optional. + Leave empty for a random one. +EOF +} + +main() { + while getopts "h" o; do + case "${o}" in + h) + print_usage + exit 0 + ;; + *) + exit 1 + ;; + esac + done + shift $((OPTIND-1)) + + if [ "$#" -eq 0 ]; then + loge "No target specified." + exit 1 + fi + TARGET_FILE="$1" + shift 1 + + local arg_command + if [ "$#" -eq 0 ]; then + loge "No command specified." + exit 1 + fi + + arg_command="$1" + shift 1 + if [ "${arg_command}" = "set-name" ]; then + handle_set_name "$@" + elif [ "${arg_command}" = "set-type" ]; then + handle_set_type "$@" + elif [ "${arg_command}" = "set-uuid" ]; then + handle_set_uuid "$@" + else + loge "Unrecognized command: ${arg_command}" + exit 1 + fi + + exit 0 +} + +main "$@" diff --git a/tools/imgcat.sh b/tools/imgcat.sh new file mode 100755 index 0000000..428c745 --- /dev/null +++ b/tools/imgcat.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env dash + +set -o errexit +set -o nounset + +. ./logging.sh + +INPUT_FILES="" +OUTPUT_FILE="" +ALIGNMENT_SECTOR_SIZE="" +ALIGNMENT_SECTOR_COUNT="" +ALIGNMENT_BYTES="" + +calc_parts_total_size() { + local total_aligned_blocks=0 + while IFS= read -r line; do + if [ -z "${line}" ]; then + continue + fi + ifile_aligned_blocks="$(du --apparent-size -B ${ALIGNMENT_BYTES} "${line}" | cut -f 1)" + logi "Input: ${ifile_aligned_blocks} blocks: ${line}" + total_aligned_blocks="$((total_aligned_blocks+ifile_aligned_blocks))" + done << EOF +$INPUT_FILES +EOF + echo "${total_aligned_blocks}" +} + +probe_alignment() { + local TEMP_FILE + TEMP_FILE="$(mktemp)" + truncate -s 1G "${TEMP_FILE}" + ALIGNMENT_SECTOR_SIZE="$(sgdisk -p "${TEMP_FILE}" | sed -rn 's/Sector size \(logical\): ([0-9]+) bytes/\1/p')" + ALIGNMENT_SECTOR_COUNT="$(sgdisk -p "${TEMP_FILE}" | sed -rn 's/Partitions will be aligned on ([0-9]+)-sector boundaries/\1/p')" + ALIGNMENT_BYTES="$((ALIGNMENT_SECTOR_SIZE*ALIGNMENT_SECTOR_COUNT))" + rm "${TEMP_FILE}" + logd "Probed alignment: ${ALIGNMENT_BYTES}B (${ALIGNMENT_SECTOR_SIZE}B*${ALIGNMENT_SECTOR_COUNT})" +} + +print_usage() { + # Keep help message within 80 cols for special consoles. + cat << EOF +Concatenate multiple partitions into a single GUID disk image. +This script only handles the image creation. +Usage: $0 [-hp] [-i input [-i input [...]]] output + -h Print this help message. + -p Print the alignment, in bytes + -i input Partition images to be included. + Multiple inputs are arranged as the specified order. + It is recommended to have input file size rounded according + to the alignment requirements, as the partition will always + be placed at alignment boundaries. + output The produced disk image. +Environment Variables: + LOG_LEVEL Controls the log level of this script. + One of: DEBUG, INFO, WARN, ERROR + Defaults to: INFO +EOF +} + +main() { + # Handling args + while getopts "i:hp" o; do + case "${o}" in + h) + print_usage + exit 0 + ;; + i) + INPUT_FILES="${INPUT_FILES}${OPTARG} +" + ;; + p) + probe_alignment + echo -n "${ALIGNMENT_BYTES}" + exit 0 + ;; + *) + exit 1 + ;; + esac + done + shift $((OPTIND-1)) + OUTPUT_FILE="${1:-}" + + # Validate inputs + if [ -z "${OUTPUT_FILE}" ]; then + loge "No output file is specified!" + exit 1 + fi + if [ -z "${INPUT_FILES}" ]; then + loge "No input file is specified!" + exit 1 + fi + + probe_alignment + logi "Using alignment: ${ALIGNMENT_SECTOR_COUNT} sectors @ ${ALIGNMENT_SECTOR_SIZE}B" + + local target_image_content_size + target_image_content_size="$(calc_parts_total_size)" + + # One block is reserved at both beginning and ending, for GPT tables. + # Final image size will be target_image_content_size+2 blocks + local final_image_blocks="$((target_image_content_size+2))" + local final_image_size_b="$((final_image_blocks*ALIGNMENT_BYTES))" + logi "Allocating output image (${final_image_blocks} blocks, ${final_image_size_b}B) at ${OUTPUT_FILE}" + truncate -s "${final_image_size_b}" "${OUTPUT_FILE}" + + logi "Writing GUID Partition Table" + 1>/dev/null sgdisk -Z "${OUTPUT_FILE}" + 1>/dev/null sgdisk -G "${OUTPUT_FILE}" + + local curr_part_idx + curr_part_idx=0 + local curr_part_start_sector + curr_part_start_sector="${ALIGNMENT_SECTOR_COUNT}" + while IFS= read -r line; do + if [ -z "${line}" ]; then + continue + fi + curr_part_idx="$((curr_part_idx+1))" + local curr_part_blocks + curr_part_blocks="$(du --apparent-size -B "${ALIGNMENT_BYTES}" "${line}" | cut -f 1)" + local curr_part_sectors="$((curr_part_blocks*ALIGNMENT_BYTES/ALIGNMENT_SECTOR_SIZE))" + logi "Create partition ${curr_part_idx}: ${curr_part_sectors} sectors @ ${curr_part_start_sector} sector" + 1>/dev/null sgdisk --new "${curr_part_idx}:${curr_part_start_sector}:+${curr_part_sectors}" "${OUTPUT_FILE}" + logi "Copy contents to part ${curr_part_idx} from ${line}" + 2>/dev/null dd if="${line}" of="${OUTPUT_FILE}" bs="${ALIGNMENT_SECTOR_SIZE}" seek="${curr_part_start_sector}" count="${curr_part_sectors}" conv=notrunc iflag=fullblock + curr_part_start_sector="$((curr_part_start_sector+curr_part_sectors))" + done << EOF +$INPUT_FILES +EOF + + exit 0 +} + +main "$@" diff --git a/tools/logging.sh b/tools/logging.sh new file mode 100644 index 0000000..1eab804 --- /dev/null +++ b/tools/logging.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env false + +readonly LOGLEVEL_DEBUG=0 +readonly LOGLEVEL_INFO=1 +readonly LOGLEVEL_WARN=2 +readonly LOGLEVEL_ERROR=3 + +__logparselevel() { + if [ -z "${LOG_LEVEL:-}" ]; then + echo $LOGLEVEL_INFO + elif [ "${LOG_LEVEL}" = "DEBUG" ]; then + echo $LOGLEVEL_DEBUG + elif [ "${LOG_LEVEL}" = "INFO" ]; then + echo $LOGLEVEL_INFO + elif [ "${LOG_LEVEL}" = "WARN" ]; then + echo $LOGLEVEL_WARN + elif [ "${LOG_LEVEL}" = "ERROR" ]; then + echo $LOGLEVEL_ERROR + else + echo $LOGLEVEL_INFO + fi +} + +__logprintraw() { + local formated_msg="$1" + >&2 echo "${formated_msg}" +} + +__logprint() { + local level_fmt="$1" + local msg="$2" + + local curr_time; curr_time="[$(date)]" + local formated_msg="${curr_time}${level_fmt}: ${msg}" + __logprintraw "${formated_msg}" +} + +logd() { + if [ "$(__logparselevel)" -le "$LOGLEVEL_DEBUG" ]; then + __logprint "[DEBUG]" "$1" + fi +} + +logdraw() { + if [ "$(__logparselevel)" -le "$LOGLEVEL_DEBUG" ]; then + __logprint "[DEBUG]" "raw print:" + __logprintraw "$1" + fi +} + +logi() { + if [ "$(__logparselevel)" -le "$LOGLEVEL_INFO" ]; then + __logprint "[INFO ]" "$1" + fi +} + +logw() { + if [ "$(__logparselevel)" -le "$LOGLEVEL_WARN" ]; then + __logprint "[WARN ]" "$1" + fi +} + +loge() { + if [ "$(__logparselevel)" -le "$LOGLEVEL_ERROR" ]; then + __logprint "[ERROR]" "$1" + fi +}