#!/bin/bash -xe PS4='+ ($LINENO) ' # To ease debugging VERSION="1.3-emac-rc2" # Config # ########## # Failures happens if $WORKDIR has "nodev" mount option WORKDIR=./work # sudo rm -r this folder if you want to re-run everything DLDIR=./downloads OUTDIR=./out #OUTUSB=/dev/sdb # If enabled, it could wreck everything there ! LEGACY=n # make USB bootable key compatible with non UEFI-BIOS DEVEL_MODE=n # Adds debugging tools in the generated image INCLUDE_TCPDUMP=y # tcpdump costs few Mb with libcrypto (but lspci depends on it also) INCLUDE_PERF=n # perf costs few Mb with libpython2.7 and so ROOTCMD=sudo WGET="wget" # "wget --no-check-certificate" could help but is a security concern # Should be customized with your NFS server IP address or options NFS_MOUNT_CMDLINE="mount -v -t nfs -o nolock eficast-nfs.mines-albi.fr:/masters /mnt/nfs" # You probably need to tweak version numbers in following URLs if you have an HTTP 404 - Not found error KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.18.4.tar.xz # needs Kconfiglib with "option module" => "module" support #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.7.7.tar.xz # ok on real hw with debian10, ok qemu with debian 11 #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.2.9.tar.xz # ok in qemu, seems sane on real hardware #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.20.17.tar.xz # ok in qemu, seems sane on real hardware #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.18.140.tar.xz # ok in qemu #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.12.74.tar.xz # reset loop in qemu # old kernels 2.6, 3.x before year 2017 don't cope with recent distro/gcc defaults options (-fPIE) # https://unix.stackexchange.com/questions/436071/compile-old-kernel-with-new-gcc # https://lore.kernel.org/patchwork/patch/731680/ #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.11.9.tar.xz # Kconfiglib v12.13.0 can't cope with it, and gcc probably too recent #KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v2.6/linux-2.6.39.4.tar.xz # KConfigLib allows to create kernel .config in a programmatic manner, in a somewhat portable way # Main project URL is https://github.com/ulfalizer/Kconfiglib #KCONFIGLIB_MAIN_URL=https://raw.githubusercontent.com/ulfalizer/Kconfiglib/v14.1.0/kconfiglib.py KCONFIGLIB_MAIN_URL=https://raw.githubusercontent.com/helmutg/Kconfiglib/bug-modules/kconfiglib.py # Provisoire, ulfalizer n'a pas mergé le patch nécessaire pour "option module" => "module" KCONFIGLIB_PATCH_URL=https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch NIC_FIRMWARE_URL=http://fr.archive.ubuntu.com/ubuntu/pool/main/l/linux-firmware/nic-firmware_1.187_all.udeb BUSYBOX_BIN_URL=https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 PCI_IDS_URL=https://pci-ids.ucw.cz/v2.2/pci.ids USB_IDS_URL=https://usb-ids.gowdy.us/usb.ids # Free space checking to not let everything go wrong ROOT_MIN_AVAIL_MIO=256 WORKDIR_MIN_AVAIL_MIO=2560 DLDIR_MIN_AVAIL_MIO=512 OUTDIR_MIN_AVAIL_MIO=64 # Utilities # ############# function check_free_space { limit_mio=$1 path="$2" LANG=C df "$path" | tail -1 | ( set +x read dev total used avail percent mp avail=${avail:--1024} avail_mio=$(($avail/1024)) if [ "$avail_mio" -lt "$limit_mio" ] then echo "$mp ($dev) should have more free space $avail_mio Mio < $limit_mio Mio)" exit 2 fi ) } function add_initrd_script { [ "$WORKDIR/initrd/$1" -nt "$0" ] || ( echo "#!/bin/busybox sh" ; cat ) > "$WORKDIR/initrd/$1" && chmod +x "$WORKDIR/initrd/$1" } # From https://landley.net/writing/rootfs-programming.html # Its first argument is the new directory, and the rest of its arguments are executables to copy. function mkchroot { [ $# -lt 2 ] && return 0 dest=$1 shift for i in "$@" do # Get an absolute path for the file p=$i [ "${p:0:1}" == "/" ] || p=$(which $i) || true if [ ! -e "$p" ] then echo "mkchoot not found: $i" return 1 fi # Skip files that already exist at target. [ -s "$dest/$p" ] && continue # Create destination path d=$(echo "$p" | grep -o '.*/') && mkdir -p "$dest/$d" && # Copy file echo + cp --dereference --preserve=mode "$p" "$dest/$p" && cp --dereference --preserve=mode "$p" "$dest/$p" && # Recursively copy shared libraries' shared libraries. mkchroot "$dest" $(ldd "$p" | egrep -o '/.* ') || return $? done } # Environement and dependencies # ################################# which lsb_release || $ROOTCMD apt install lsb-release codename=$(lsb_release -sc || true) if [ "x$codename" != "xbullseye" ] then cat >&2 < "$DLDIR/apt-update-done" fi if [ ! -e "$WORKDIR/apt-install-done" ] then check_free_space $ROOT_MIN_AVAIL_MIO / # Dependencies of this script (assuming default debian install or live) $ROOTCMD apt-get install wget libncurses5-dev coreutils [ "x$LEGACY" == "xy" ] && $ROOTCMD apt-get install mbr syslinux # Dependencies for kernel building $ROOTCMD apt-get install build-essential flex bison libelf-dev # Dependencies for kernel tools [ "x$DEVEL_MODE" == "xy" ] && $ROOTCMD apt-get install libunwind-dev \ libdw-dev libaudit-dev libssl-dev libslang2-dev libnuma-dev \ systemtap-sdt-dev python-dev binutils-dev libiberty-dev libbabeltrace-ctf-dev # Optionnally qemu to run the result for santity checking [ "x$DEVEL_MODE" = "xy" ] && $ROOTCMD apt-get install qemu-system-x86 # Dependencies to put into the initrd # util-linux : blkdiscard, fdisk, sfdisk, lsblk, setterm # net-tools : mii-tool # ncurses-bin : tput $ROOTCMD apt-get install dmidecode pciutils usbutils lshw sysstat iftop strace rsync \ ntfs-3g e2fsprogs dosfstools exfat-utils partclone util-linux gdisk efibootmgr \ pv bc figlet toilet-fonts ncurses-bin beep net-tools ethtool pigz tmux iperf # Optionnal dependencies to put into the initrd [ "x$INCLUDE_TCPDUMP" == "xy" ] && $ROOTCMD apt-get install tcpdump > "$WORKDIR/apt-install-done" fi # Kernel build setup # ###################### kernel_tarball=$DLDIR/$(basename $KERNEL_TARBALL_URL) [ -s "$kernel_tarball" ] || $WGET -O "$kernel_tarball" "$KERNEL_TARBALL_URL" if [ ! -s "$WORKDIR/kernel/Makefile" ] then mkdir -p "$WORKDIR/kernel" tar xf "$kernel_tarball" --strip-components=1 -C "$WORKDIR/kernel" fi if [ ! -s "$WORKDIR/kernel/scripts/Kconfiglib/kconfiglib.py" ] then [ -s "$DLDIR/kconfiglib.py" ] || $WGET -O "$DLDIR/kconfiglib.py" "$KCONFIGLIB_MAIN_URL" [ -s "$DLDIR/makefile.patch" ] || $WGET -O "$DLDIR/makefile.patch" "$KCONFIGLIB_PATCH_URL" mkdir -p "$WORKDIR/kernel/scripts/Kconfiglib" patch -t -p1 -d "$WORKDIR/kernel" < "$DLDIR/makefile.patch" cp "$DLDIR/kconfiglib.py" "$WORKDIR/kernel/scripts/Kconfiglib/kconfiglib.py" fi cat >"$WORKDIR/kernel/scripts/Kconfiglib/customize.py" <<"EOT" import sys from kconfiglib import Kconfig, standard_config_filename, TRI_TO_STR, TRISTATE def sset(sym, value=None): # Default value if value == None: if sym.assignable: # find highest possible assignable value (last item of modifiable sorted tuple) value = sym.assignable[-1] else: print('%s is not modifiable at all for now'%sym.name) return True # Sanity check if isinstance(value, int) and value not in sym.assignable: print('%s can\'t be set to %s for now'%(sym.name,TRI_TO_STR[value])) return True # Idempotency check if isinstance(value, int): old_value = sym.tri_value else: old_value = sym.str_value if old_value == value: # No more_work return False # Set value if isinstance(value, int): print('%s=%s [was: %s]'%(sym.name,TRI_TO_STR[value],TRI_TO_STR[old_value])) else: print('%s=%s [was: %s]'%(sym.name,value,old_value)) sym.set_value(value) # plausible more_work to do return True kconf = Kconfig(sys.argv[1]) kconf.load_config(standard_config_filename()) debug = '--debug' in sys.argv; passes = 5 support_xz = 'HAVE_KERNEL_XZ' in kconf.syms print('support_xz == %r'%support_xz) i = 0 more_work = True while more_work and i < passes: more_work = False i += 1 print('Kconfiglib/customize.py pass %i'%i) for sym in kconf.defined_syms: # Default hostname is (none) and could make FreeBSD's dhcpd complain because unallowed '()' if sym.name == 'DEFAULT_HOSTNAME': more_work = sset(sym, 'eficast') or more_work # Embed initrd in the EFI bootable kernel if sym.name == 'INITRAMFS_SOURCE': more_work = sset(sym, '../initrd/') or more_work # Support dumb hotplug via /sbin/hotplug script (disabled by default on 5.x kernels, may be some 4.x also) # We don't ship mdev/udev/systemd or any netlink listener to load appropriate modules for now if sym.name == 'UEVENT_HELPER': more_work = sset(sym) or more_work if sym.name == 'UEVENT_HELPER_PATH': more_work = sset(sym, '/sbin/hotplug') or more_work # Make kernel directly loadable by EFI, add USB3 if sym.name in ['EFI_STUB', 'EARLY_PRINTK_EFI']: more_work = sset(sym) or more_work if sym.name in ['EFI_VARS', 'USB_XHCI_HCD']: more_work = sset(sym, 1) or more_work # Support FUSE, NVMe, soft RAID (linux) and hard RAID (some cards) if sym.name in ['FUSE_FS', 'DM_RAID', 'MEGARAID_SAS']: more_work = sset(sym, 1) or more_work if sym.name in ['BLK_DEV_NVME', 'SCSI_LOWLEVEL', 'MEGARAID_NEWGEN']: more_work = sset(sym) or more_work # Support for framebuffer because EFI+ text mode don't always display something if sym.name in ['FB', 'FB_VESA', 'FRAMEBUFFER_CONSOLE', 'FRAMEBUFFER_CONSOLE_DETECT_PRIMARY']: more_work = sset(sym) or more_work # If --debug passed as arg, make kernel aware of virtual drivers (used for testing eficast on qemu/kvm) if debug and sym.name in ['VIRTIO_PCI', 'VIRTIO_MMIO', 'VIRTIO_NET', 'VIRTIO_BLK', 'SCSI_LOWLEVEL', 'SCSI_VIRTIO']: more_work = sset(sym) or more_work # Disable thing that are unneeded or annoying for the purpose of disk cloning if sym.name in [ 'HAMRADIO', 'HIBERNATION', 'NETFILTER', 'PCCARD', 'RFKILL', 'SECURITY', 'SOUND', 'SUSPEND', 'VIRTUALIZATION', 'WIRELESS', 'WLAN']: more_work = sset(sym, 0) or more_work # Following generic actions should done only on visible TRISTATE symbols if sym.type == TRISTATE and sym.visibility > 0: # Build all available net/ethernet drivers if True in [ ('drivers/net/ethernet' in node.filename) for node in sym.nodes ]: if sym.assignable and 1 in sym.assignable: more_work = sset(sym, 1) or more_work # Following tunings are not mandatory # Try at each pass but don't create an extra pass if nothing else as asked for # To "do" this, don't set more_work var # If --debug passed as arg, make kernel aware of virtual drivers (used for testing eficast on qemu/kvm) if debug and sym.name in ['VIRTIO_PCI', 'VIRTIO_MMIO', 'VIRTIO_NET', 'VIRTIO_BLK', 'SCSI_VIRTIO']: sset(sym, 1) #sset(sym) # Compress everything with XZ if available (slower, smaller) if support_xz: if sym.name in ['KERNEL_XZ', 'RD_XZ', 'INITRAMFS_COMPRESSION_XZ']: sset(sym) if sym.name in ['RD_GZIP', 'RD_BZIP2', 'RD_LZMA', 'RD_LZO', 'RD_LZ4']: sset(sym, 0) if sym.name == 'INITRAMFS_COMPRESSION': sset(sym, '.xz') # Enable beeps (pcspkr) and .config to be readable from /proc/config.gz if sym.name in ['IKCONFIG','IKCONFIG_PROC', 'INPUT_PCSPKR']: sset(sym) # Try to get rid of some useless features in EFIcast context if sym.name in ['IP_ADVANCED_ROUTER']: sset(sym, 0) # Write .config even if some symbols are unset msg = kconf.write_config(standard_config_filename()) print(msg) res = 0 if not isinstance(msg, str) or not msg.startswith('Configuration saved'): res = 2 if i == passes: print('ERROR : can\'t set some of kernel config symbols after %i passes'%passes) res = 1 sys.exit(res) EOT chmod +x "$WORKDIR/kernel/scripts/Kconfiglib/customize.py" # Kernel prepare + make tools # ############################### if [ ! -s "$WORKDIR/kernel/.config" ] then pushd "$WORKDIR/kernel" make defconfig if [ "x$DEVEL_MODE" == "xy" ] then extra="SCRIPT_ARG=--debug" else extra="" fi make scriptconfig PYTHONCMD=python3 SCRIPT=scripts/Kconfiglib/customize.py $extra || ( mv .config .config-broken; exit 3 ) popd fi if [ "x$INCLUDE_PERF" == "xy" -a ! -s "$WORKDIR/kernel/tools/perf/perf" ] then pushd "$WORKDIR/kernel" # Workaround : linux-3.16.57 (and others?) have make tools/perf break in some cases, ignore it make tools/perf || true popd fi # Initial Ram Disk building (embed in kernel) # ############################################### if [ ! -s "$WORKDIR/initrd/etc/group" ] then mkdir -p "$WORKDIR/initrd/"{bin,dev,etc/rc.d,mnt/nfs,root,proc,root,sbin,sys,run/lock,run,tmp,usr/share/udhcpc,var/log} $ROOTCMD cp -a /dev/{null,console,tty1} "$WORKDIR/initrd/dev/" $ROOTCMD chmod 1777 "$WORKDIR/initrd/run/lock" touch "$WORKDIR/initrd/etc/fstab" ln -s "/proc/mounts" "$WORKDIR/initrd/etc/mtab" ln -s "../run" "$WORKDIR/initrd/var/run" ln -s "../run/lock" "$WORKDIR/initrd/var/lock" echo 'root::0:0:root:/root:/bin/sh' > "$WORKDIR/initrd/etc/passwd" echo 'root:x:0:' > "$WORKDIR/initrd/etc/group" fi # XXX workaround, kernel makefile's cpio preseves everything. Consider using fakeroot $ROOTCMD chown -R $USER: "$WORKDIR/initrd" [ -s "$DLDIR/busybox" ] || $WGET -O "$DLDIR/busybox" "$BUSYBOX_BIN_URL" cp "$DLDIR/busybox" "$WORKDIR/initrd/bin/busybox" chmod 0755 "$WORKDIR/initrd/bin/busybox" ln -sf /bin/busybox "$WORKDIR/initrd/init" cp -a /etc/localtime "$WORKDIR/initrd/etc/" if [ ! -s "$WORKDIR/initrd/etc/keys.bmap" ] then $ROOTCMD dumpkeys | $ROOTCMD loadkeys -ub > "$WORKDIR/initrd/etc/keys.bmap" fi if [ ! -s "$WORKDIR/initrd/usr/bin/rsync" ] then ( set +x PATH="$WORKDIR/kernel/tools/perf:/usr/sbin:/usr/bin:/sbin:/bin" # Diagnostic tools mkchroot "$WORKDIR/initrd" dmidecode iftop iostat lshw lspci lsblk lsusb mpstat iperf # Console tools and manpages display mkchroot "$WORKDIR/initrd" tput setterm strace groff nroff troff grotty gtbl tmux bc pv figlet beep # Filesystem tools mkchroot "$WORKDIR/initrd" mkfs mke2fs /sbin/mkfs.ext* mkntfs mkfs.ntfs mkfs.fat mkexfatfs mkfs.exfat mkfs mkchroot "$WORKDIR/initrd" ntfs-3g mount.ntfs mount.fuse mount.exfat-fuse mkchroot "$WORKDIR/initrd" /sbin/ntfs* /bin/ntfs* # Network tools mkchroot "$WORKDIR/initrd" mii-tool ethtool # Disk tools mkchroot "$WORKDIR/initrd" blkdiscard fdisk gdisk sfdisk sgdisk # Cloning tools mkchroot "$WORKDIR/initrd" /usr/sbin/partclone* efibootmgr pigz scp rsync # Some dyn-loaded libraries (ldd will not display them) mkchroot "$WORKDIR/initrd" /lib/x86_64-linux-gnu/libusb-1.0.so.* if [ "x$INCLUDE_TCPDUMP" == "xy" ] then # tcpdump costs few Mb with libcrypto mkchroot "$WORKDIR/initrd" tcpdump fi ) fi # Some needed data files if [ ! -s "$WORKDIR/initrd/usr/share/figlet/standard.flf" ] then cp -ra /lib/terminfo "$WORKDIR"/initrd/lib/ mkdir -p "$WORKDIR"/initrd/usr/lib/locale "$WORKDIR"/initrd/usr/share/figlet cp -ra /usr/lib/locale/C.UTF-8 "$WORKDIR"/initrd/usr/lib/locale/ cp -a /usr/share/figlet/{standard,mono12}* "$WORKDIR"/initrd/usr/share/figlet/ fi # Perf tool : skip copy and deps if compilation has failed or was skipped p="$WORKDIR/kernel/tools/perf/perf" if [ -s "$p" ] then ( cp -a "$p" "$WORKDIR/initrd/sbin/" set +x mkchroot "$WORKDIR/initrd" $(ldd "$p" | egrep -o '/.* ') ) fi if [ ! -f "$WORKDIR/initrd/etc/groff/man.local" ] then mkdir -p "$WORKDIR"/initrd/usr/man/man{1,6,8} "$WORKDIR"/initrd/usr/share/groff/current/font mkdir -p "$WORKDIR"/initrd/etc/groff/ cp -a /usr/share/man/man1/{beep,iostat,lshw,mpstat,setterm,bc,pv,strace,tmux,pigz}* "$WORKDIR/initrd/usr/man/man1/" cp -a /usr/share/man/man6/figlet* "$WORKDIR/initrd/usr/man/man6/" cp -a /usr/share/man/man8/{dmidecode,iftop,lspci,lsblk,lsusb,partclone,efibootmgr,mkfs}* "$WORKDIR/initrd/usr/man/man8/" cp -a /usr/share/man/man8/{ntfs,mkntfs,mkexfatfs,mount.ntfs,mount.fuse,mount.exfat-fuse}* "$WORKDIR/initrd/usr/man/man8/" cp -a /usr/share/man/man8/{mount.exfat,fdisk,gdisk,sfdisk,sgdisk,tcpdump,mii-tool,ethtool}* "$WORKDIR/initrd/usr/man/man8/" cp -ra /usr/share/groff/current/font/devascii "$WORKDIR/initrd/usr/share/groff/current/font/" cp -ra /usr/share/groff/current/tmac "$WORKDIR/initrd/usr/share/groff/current/" cp -a /etc/groff/man.local "$WORKDIR/initrd/etc/groff/" fi if [ ! -s "$WORKDIR/initrd/usr/share/misc/pci.ids" ] then [ -s "$DLDIR/pci.ids" ] || $WGET -O "$DLDIR/pci.ids" "$PCI_IDS_URL" [ -s "$DLDIR/usb.ids" ] || $WGET -O "$DLDIR/usb.ids" "$USB_IDS_URL" mkdir -p "$WORKDIR/initrd/var/lib/usbutils" "$WORKDIR/initrd/usr/share/misc" cp "$DLDIR/usb.ids" "$WORKDIR/initrd/var/lib/usbutils/" cp "$DLDIR/pci.ids" "$WORKDIR/initrd/usr/share/misc/" fi if [ ! -d "$WORKDIR/firmware" ] then [ -s "$DLDIR/nic-firmware.deb" ] || $WGET -O "$DLDIR/nic-firmware.deb" "$NIC_FIRMWARE_URL" mkdir -p "$WORKDIR/firmware" dpkg -x "$DLDIR/nic-firmware.deb" "$WORKDIR/firmware/" fi if [ ! -d "$WORKDIR/initrd/lib/firmware/rtl_nic" ] then mkdir -p "$WORKDIR/initrd/lib/firmware" cp -r "$WORKDIR/firmware/lib/firmware/rtl_nic" "$WORKDIR/initrd/lib/firmware/" fi echo $VERSION > "$WORKDIR/initrd/etc/eficast_version" cat > "$WORKDIR/initrd/etc/rc.d/funcs" <<"EOF" # echo_color [prefix_string] echo_color() { setterm --foreground "$1" --background "$2" --bold on echo -n "$3" setterm --foreground white --background black --bold off [ "x$4" == "xversion" ] && echo " (eficast v"$(cat /etc/eficast_version)")" || echo } # no args, print colored message, wait 10 sec and reboot eficast_end() { echo -e "\e]2;eficast_end\007" # Term title (tmux), intentionnal carriage return ( sfx_success ; touch /run/nosound ) & message success [ -r /run/eficast_end ] && action=$(cat /run/eficast_end) case $action in poweroff) read -t10 -p 'Ctrl+C to have a shell, Enter to skip wait time for poweroff...' wait # if beep command is killed from sfx_success, internal speaker can beep for a long anoying time poweroff ;; reboot) read -t10 -p 'Ctrl+C to have a shell, Enter to skip wait time for reboot...' wait # if beep command is killed from sfx_success, internal speaker can beep for a long anoying time reboot ;; *) echo "Dropping a shell (consider putting poweroff or reboot in /run/eficast_end)" PS1='\h:\w# ' HOME='/root/' /bin/busybox sh ;; esac } # note : rcS rescue_shell is slighly different because most of the env is not ready in rcS rescue_shell() { echo -e "\e]2;rescue_shell\007" # Term title (tmux), intentionnal carriage return message rescue echo_color white red "Something went wrong. Dropping to a shell." version ( sfx_failure ; touch /run/nosound ) & PS1='\h:\w# ' HOME='/root/' busybox sh } machine_info() { setterm -bold on grep -F MemTotal: /proc/meminfo for k in system-manufacturer system-product-name \ baseboard-manufacturer baseboard-product-name \ bios-version bios-release-date do echo $k: $(dmidecode -s $k) done lspci -nn | cut -d' ' -f2- | sed -ne 's/^Ethernet[^:]*/network-card/p' ip -o l | sed -ne 's/[0-9]*: \([^:]*\):[^\\]*\\\s*link\/ether\s/network-mac-\1: /p' lsblk -dnl --output TRAN,PATH,SIZE,RO,TYPE,MODEL,REV | sort | sed 's/^/disk: /' #lsusb 2>/dev/null | grep -vE hub$ | cut -d: -f2- | sed 's/^ ID/usb-device:/' setterm -bold off } network_up() { echo ip link set dev lo up ip link set dev lo up ip -oneline link | grep DOWN | cut -d: -f2 | grep -v sit | grep -v lo | while read iface do # BugFix Intel 217LM + WakeOnLan + dual-boot Windows 10 # After Windows hibernation or fast boot powerdown, link goes up but no network packets go out of the chip d=/sys/class/net/$iface/device if [ 0x153a = $(cat $d/device) ] then echo 1 \> $d/reset echo 1 > $d/reset fi # BugFix Realtek 8168 + dual-boot Windows 10 # After reboot to linux, linux kernel don't load firmware, link never came up if [ 0x8168 = $(cat $d/device) ] then echo 1 \> $d/reset echo 1 > $d/reset fi echo ip link set dev $iface up ip link set dev $iface up done while ! ip -oneline link | grep LOWER_UP | cut -d: -f2 | grep -v sit | grep -v lo do sleep 1 done } network_conf() { pidof udhcpc >/dev/null && killall udhcpc # Reap background udhcpc in case of rc2 retry ip -oneline link | grep LOWER_UP | cut -d: -f2 | grep -v sit | grep -v lo | while read iface do udhcpc -b $iface -t 15 done } network_show() { setterm -bold on ip -o addr show | sed -ne 's/[0-9]*:\s*\(\S*\)\s*inet6*\s\(\S*\)\s.*$/\1: \2/p' setterm -bold off } notes() { echo 'print "n=CCDEEFFGAABB\na=nsnbnnsnbnbn\n(\n"; for (d=21;d<109;d++) { scale=20; f=440*e((d-69)/12*l(2)); fr=f+0.5; scale=0; fr=fr/1; o=d/12-1; t=d%12; print "echo ${n:",t,":1}${a:",t,":1}",o,"=",fr,"\n" }; print ") | tr -d n"' | bc -l | busybox sh } sound() { [ -f /run/nosound ] && return 0 mode=$1; dur=$2; shift 2; args="-l0" while [ -n "$1" ]; do case $mode in 1) args="$args -n -f$1 -l$dur";; 2) args="$args -n -f$1 -l$(($2*dur))";; 3) args="$args -n -f$1 -l$(($2*dur)) -D$(($3*dur))";; esac shift $mode done beep $args } sfx_question() { : ; } sfx_success() { sound 2 50 $E7 2 $C7 3; } sfx_failure() { sound 1 600 $G4 ; } sfx_starting() { : ; } eval $(notes) EOF # All shell spawned here must be login-shell, will load /etc/profile cat > "$WORKDIR/initrd/etc/inittab" <<"EOF" # Custom init scripts ::sysinit:/etc/rc.d/rcS # Standard things follow ::ctrlaltdel:/sbin/reboot -f ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r ::restart:/sbin/init tty1::respawn:/bin/sh -lc /etc/rc.d/rc2-or-rescue tty2::askfirst:/bin/sh -l tty3::askfirst:/bin/sh -l EOF cat > "$WORKDIR/initrd/etc/profile" <<"EOF" export LC_ALL=C.UTF-8 export TERM=linux # busybox on qemu set vt102, then tmux segfaults # https://git.busybox.net/busybox/tree/init/init.c, search for VT_OPENQRY # happens if qemu pass "-append console=ttyS0" or similar to the kernel EOF add_initrd_script etc/rc.d/rcS <<"EOF" echo -e '\e[32m/etc/rc.d/rcS script will run on /dev/console now\e[0m' echo -e '\e[37;43m\e[1m'----- rcS script started -----'\e[0m' # Hint for user about boot steps if its hangs # Declare some funcs to have a tidy output with trace mode # note : rc.d/funcs has a rescue_shell() that is slighly different (the environment is not fully intialized here) rescue_shell() { /bin/busybox echo -e '\e[37;41m\e[1m'Something went wrong. Dropping to a shell.'\e[0m' /bin/busybox beep PS1='\h:\w# ' busybox setsid busybox cttyhack busybox sh /bin/busybox sync; busybox umount /dev/pts /dev /sys /proc /bin/busybox reboot -f } mount_pseudofilesystems() { # Mount pseudo-filesystems mount -t proc none /proc || return $? mount -t sysfs none /sys || return $? mount -t devtmpfs -o size=1m none /dev || return $? mkdir /dev/pts || return $? mount -t devpts none /dev/pts || return $? # Don't fail if efivarfs unavailable (boot in legacy BIOS mode) modprobe efivarfs || true mount -t efivarfs efivarfs /sys/firmware/efi/efivars || true ln -s /proc/self/fd/2 /dev/stderr return 0 } # TODO Kernel 5.x disable UEVENT_HELPER by default, it may need some clean alternative coldplugging() { echo 4 > /proc/sys/kernel/printk for d in /sys/bus/*/devices/* do [ -d "$d" ] && cd "$d" && [ -r driver ] || echo add > uevent done sleep 3 cut -f4 /proc/sys/kernel/printk > /proc/sys/kernel/printk } # Trace execution set -v /bin/busybox --install -s||rescue_shell # Setup busybox symlinks for all applets mount_pseudofilesystems || rescue_shell # Setup /dev, /proc, /sys and so klogd; syslogd # Start logging in /var/log/messages mount -o remount -o size=80% / # Allow using most of RAM for rootfs coldplugging # Load modules for cold-plugged peripherials loadkmap < /etc/keys.bmap # Load keyboard layout set +v # Hint users about boot steps to help them if it hangs echo -e '\e[32m/etc/rc.d/rc2-or-rescue script will run on /dev/tty1 now\e[0m' echo -e '\e[37;43m\e[1m----- rcS script ended -----\e[0m' EOF add_initrd_script etc/rc.d/rc2-or-rescue <<"EOF" . /etc/rc.d/funcs # Load helper functions if [ -f /run/rescue ] then sleep 1 # reboot command in eficast_end kill all processes and could trigger rescue mode rm /run/rescue echo_color red black "/etc/rc.d/rc2 dead. Spawning rescue_shell on /dev/tty1 now" rescue_shell else touch /run/rescue echo_color green black "/etc/rc.d/rc2 script will run on /dev/tty1 now" /etc/rc.d/rc2 fi EOF add_initrd_script etc/rc.d/rc2 <<"EOF" . /etc/rc.d/funcs # Load helper functions echo_color white yellow '----- rc2 script started -----' message boot-ok setterm -blank 60 # screen sleep mode after 60 minutes set -v # Trace execution network_up network_conf network_show machine_info set -e # Go in rescue shell if any further step goes wrong /etc/rc.d/initrd-autorun eficast_end EOF add_initrd_script etc/rc.d/initrd-autorun <<"EOF" echo -ne "\e]2;$0\007" # Term title (tmux) . /etc/rc.d/funcs # Load helper functions echo_color white yellow '----- initrd-autorun script started -----' echo -e '\e[32m/etc/rc.d/nfs-mount script will run now\e[0m' busybox sh -v /etc/rc.d/nfs-mount if [ $? -ne 0 ] then echo_color white red "----- NFS server : not mounted ------" echo_color green black "Check for failure above. Exiting rescue shell will retry everything" exit 1 fi if ! [ -x /mnt/nfs/nfs-autorun.sh ] then echo_color white red "----- NFS server : missing script ------" echo_color green black "/mnt/nfs/nfs-autorun.sh : should be present, readable and executable" echo ls -l /mnt/nfs ls -l /mnt/nfs exit 2 fi echo_color white green "----- NFS server ready ------" read -t10 -p 'Ctrl+C to have a shell, Enter to skip wait time...' message nfs-ok echo -ne "\e]2;nfs-autorun.sh\007" # Term title (tmux) cd /mnt/nfs # don't use source here, exit in inner script will skip cleanup routine (and rescue_shell) ./nfs-autorun.sh EOF add_initrd_script etc/rc.d/nfs-mount <&1 | /bin/busybox awk -vT="$0: DEVPATH=$DEVPATH PCI_ID==$PCI_ID " '{ print T $0 }' | /bin/busybox tee -a /var/log/hotplug-pci.log else echo -e '\e[37;43m\e[1m'$0: PCI_ID==$PCI_ID, no MODALIAS found'\e[0m' echo $0: PCI_ID==$PCI_ID, no MODALIAS found >> /var/log/hotplug-pci.log fi else if [ -n "$MODALIAS" ] then /bin/busybox modprobe -v $MODALIAS 2>&1 | /bin/busybox awk -vT="$0: DEVPATH=$DEVPATH MODALIAS==$MODALIAS " '{ print T $0 }' >> /var/log/hotplug-non-pci.log else echo "$0: DEVPATH=$DEVPATH no MODALIAS found" >> /var/log/hotplug-non-pci.log fi fi else echo "$0: DEVPATH=$DEVPATH : no support for '$ACTION'" >> /var/log/hotplug-unsupported.log fi > /dev/console 2>&1 EOF add_initrd_script bin/iftop-watch <<"EOF" echo -ne "\e]2;$*\007" # Term title (tmux) iftop -nNl EOF add_initrd_script bin/iostat-watch <<"EOF" # Emulates watch command with iostat (filtered info, minimal height) echo -ne "\e]2;$*\007" # Term title (tmux) iostat -cdmz 1 | awk '$1=="avg-cpu:"{system("clear")} length($0)>0{print}' EOF add_initrd_script bin/mpstat-watch <<"EOF" # Emulates watch command with mpstat echo -ne "\e]2;$*\007" # Term title (tmux) while true; do mpstat -I SCPU | grep -vE '^(Linux.*|)$' sleep 1 done EOF add_initrd_script bin/pigz-watch <<"EOF" # Emulate watch command using pipeview (pv) for a running pigz echo -ne "\e]2;$*\007" # Term title (tmux) while true; do pid=$(pidof -s pigz) [ -n "$pid" ] && pv -F "%N %b %T %t %r %a %p %I" -d $pid sleep 1 done EOF add_initrd_script bin/pstree-watch <<"EOF" echo -ne "\e]2;$*\007" # Term title (tmux) while true; do watch -t "pstree" sleep 1 done EOF add_initrd_script bin/lsblk-watch <<"EOF" echo -ne "\e]2;$*\007" # Term title (tmux) watch -t lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,LABEL,MOUNTPOINT EOF add_initrd_script usr/share/udhcpc/default.script <<"EOF" #!/bin/sh case $1 in bound) # Configure interface and default gateway busybox ifconfig $interface ${mtu:+mtu $mtu} $ip netmask $subnet ${broadcast:+broadcast $broadcast} busybox ip -4 route add default via $router dev $interface # Update resolver configuration file [ -n "$domain" ] && R="domain $domain" || R="" for i in $dns; do R="$R\nnameserver $i" done echo -e "$R" > /etc/resolv.conf # Update in-kernel-memory hostname [ -n "$hostname" ] && hostname $hostname ;; renew | deconf) echo "no action taken: $1: $interface" >&2 ;; leasefail | nak) echo "configuration failed: $1: $message" >&2 ;; esac EOF add_initrd_script bin/message <<"EOF" # Output some center ASCII-art text, one line per argument printf '\e]2;'"$*"'\e\\' # Term title (tmux) CMDFIG="figlet -t -f mono12" while [ -n "$1" ]; do termwidth=$(tput cols) textlen=$(echo $1 | $CMDFIG | head -n1 | wc -c) offset=$(( (termwidth-textlen)/2 )) echo $1 | $CMDFIG | awk "{printf \"%${offset}s\"; print}" shift done EOF # Kernel build (with embed initramfs) # ####################################### pushd "$WORKDIR/kernel" nproc=$(nproc --all) nproc=${nproc:-4} # This make will produce a kernel with embed initrd without modules make -j $((nproc+1)) # This will complete the inird tree with modules [ -d ../initrd/lib/modules ] && rm -r ../initrd/lib/modules INSTALL_MOD_PATH=../initrd/ make modules_install # XXX workaround, kernel makefile cpio preseves everything $ROOTCMD chown -R root: ../initrd # XXX Workaround : some kernel releases forget to update embed initramfs in certain cases [ -f usr/initramfs_data.cpio.gz ] && rm usr/initramfs_data.cpio.gz $ROOTCMD chmod -R go+rX ../initrd/lib/modules # This produce the final image make popd # Copy / run result EFI file # ############################## # Workaround : direct kernel boot with libvirt/virt-manager do a chown on BOOTX64.EFI and cp won't overwrite [ -f "$OUTDIR/BOOTX64.EFI" ] && rm -f "$OUTDIR/BOOTX64.EFI" cp "$WORKDIR/kernel/arch/x86/boot/bzImage" "$OUTDIR/BOOTX64.EFI" # Prepare an USB bootable disk if $OUTUSB is set and is has at least 1 primary partition if [ -n "$OUTUSB" -a -b "${OUTUSB}1" ] then [ -d "$WORKDIR/mountpoint" ] || mkdir "$WORKDIR/mountpoint" mount | grep -E "^${OUTUSB}1" -q && $ROOTCMD umount "${OUTUSB}1" if [ "x$LEGACY" == "xy" ] then $ROOTCMD install-mbr -f "${OUTUSB}" $ROOTCMD sfdisk --activate "$OUTUSB" 1 sleep 1 # XXX do a proper udev wait $ROOTCMD mount "${OUTUSB}1" "$WORKDIR/mountpoint" $ROOTCMD tee "$WORKDIR/mountpoint/syslinux.cfg" > /dev/null <<"EOT" default eficast label eficast kernel /EFI/BOOT/BOOTX64.EFI append from=syslinux label debug kernel /EFI/BOOT/BOOTX64.EFI append debug ignore_loglevel bootmem_debug apic=debug show_lapic=all acpi.debug_layer=0xffffffff acpi.debug_level=0xffffffff debugpat initcall_debug pnp.debug=1 sched_debug from=syslinux label noacpi kernel /EFI/BOOT/BOOTX64.EFI append noacpi from=syslinux timeout 10 prompt 1 EOT else $ROOTCMD mount "${OUTUSB}1" "$WORKDIR/mountpoint" fi $ROOTCMD mkdir -p "$WORKDIR/mountpoint/EFI/BOOT" $ROOTCMD cp "$OUTDIR/BOOTX64.EFI" "$WORKDIR/mountpoint/EFI/BOOT" $ROOTCMD umount "$WORKDIR/mountpoint" fi if [ "x$DEVEL_MODE" == "xy" ] then qemu-system-x86_64 -M q35 -m 256 -kernel "$OUTDIR/BOOTX64.EFI" -enable-kvm # -serial stdio -append "console=ttyAMA0 console=ttyS0" hangs boot on bad serial driver fi echo "$0 successful end of execution"