#!/bin/bash -xe # Config # ########## WORKDIR=/tmp/work DLDIR=./downloads OUTDIR=./out OUTUSB=/dev/sdb1 RUN_QEMU=y ROOTCMD=sudo WGET="wget --no-check-certificate" KERNEL_TARBALL_URL=https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.6.tar.xz KCONFIGLIB_MAIN_URL=https://raw.githubusercontent.com/ulfalizer/Kconfiglib/7eace27993ad3aa1d6911866d9c60a11f32d36d9/kconfiglib.py KCONFIGLIB_PATCH_URL=https://raw.githubusercontent.com/ulfalizer/Kconfiglib/7eace27993ad3aa1d6911866d9c60a11f32d36d9/makefile.patch NIC_FIRMWARE_URL=http://fr.archive.ubuntu.com/ubuntu/pool/main/l/linux-firmware/nic-firmware_1.157_all.udeb BUSYBOX_BIN_URL=https://busybox.net/downloads/binaries/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 # Utilities # ############# # 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. [ -f "$dest/$p" ] && continue # Create destination path d=$(echo "$p" | grep -o '.*/') && mkdir -p "$dest/$d" && # Copy file echo ${PS4}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 # ################################# [ -d "$WORKDIR" ] || mkdir "$WORKDIR" [ -d "$DLDIR" ] || mkdir "$DLDIR" [ -d "$OUTDIR" ] || mkdir "$OUTDIR" if [ ! -e "$WORKDIR/apt-done" ] then $ROOTCMD apt-get update # Dependencies of this script $ROOTCMD apt-get build-dep linux-source $ROOTCMD apt-get install wget libncurses5-dev # Optionnally qemu to make some santity checks [ "x$RUN_QEMU" = "xy" ] && $ROOTCMD apt-get install qemu-system-x86 # Dependencies to put into the initrd $ROOTCMD apt-get install dmidecode pciutils usbutils lshw sysstat iftop atop $ROOTCMD apt-get install partclone udpcast gdisk efibootmgr tcpdump > "$WORKDIR/apt-done" fi # Initial Ram Disk building (embed in kernel) # ############################################### if [ ! -d "$WORKDIR/initrd" ] then mkdir -p "$WORKDIR/initrd/"{bin,dev,etc,mnt,root,proc,root,sbin,sys,run/lock,tmp,var} $ROOTCMD cp -a /dev/{null,console,tty1} "$WORKDIR/initrd/dev/" $ROOTCMD chmod 1777 "$WORKDIR/initrd/run/lock" ln -s "../run" "$WORKDIR/initrd/var/run" ln -s "../run/lock" "$WORKDIR/initrd/var/lock" fi if [ ! -f "$WORKDIR/initrd/bin/busybox" ] then [ -f "$DLDIR/busybox" ] || $WGET -O "$DLDIR/busybox" "$BUSYBOX_BIN_URL" cp "$DLDIR/busybox" "$WORKDIR/initrd/bin/busybox" chmod +x "$WORKDIR/initrd/bin/busybox" fi if [ ! -f "$WORKDIR/initrd/etc/keys.bmap" ] then $ROOTCMD dumpkeys | $ROOTCMD loadkeys -b > "$WORKDIR/initrd/etc/keys.bmap" fi ( set +x PATH="/usr/sbin:/usr/bin:/sbin:/bin" # Diagnostic tools mkchroot "$WORKDIR/initrd" atop dmidecode iftop iostat lshw lspci lsusb mpstat tcpdump # Manpage display mkchroot "$WORKDIR/initrd" strace groff nroff troff grotty gtbl # Cloning tools mkchroot "$WORKDIR/initrd" /usr/sbin/partclone* efibootmgr gdisk udp-receiver # Some dyn-loaded libraries (ldd will not display them) mkchroot "$WORKDIR/initrd" /lib/x86_64-linux-gnu/libusb-1.0.so.0 ) #if [ ! -L "$WORKDIR/initrd/lib/x86_64-linux-gnu/tls/x86_64" ] #then mkdir -p "$WORKDIR/initrd/lib/x86_64-linux-gnu/tls" # ln -s "../.." "$WORKDIR/initrd/lib/x86_64-linux-gnu/tls/x86_64" #fi if [ ! -d "$WORKDIR/initrd/usr/man" ] then mkdir -p "$WORKDIR"/initrd/usr/man/man{1,8} "$WORKDIR"/initrd/usr/share/groff/1.22.2/font "$WORKDIR/initrd/etc/groff" cp /usr/share/man/man1/{atop.1,iostat,mpstat,strace,udp-receiver}* "$WORKDIR/initrd/usr/man/man1/" cp /usr/share/man/man8/{dmidecode,partclone,efibootmgr,gdisk,iftop,tcpdump}* "$WORKDIR/initrd/usr/man/man8/" cp -r /usr/share/groff/1.22.2/font/devascii "$WORKDIR/initrd/usr/share/groff/1.22.2/font/" cp -r /usr/share/groff/1.22.2/tmac "$WORKDIR/initrd/usr/share/groff/1.22.2/" cp /etc/groff/man.local "$WORKDIR/initrd/usr/share/groff/1.22.2/" fi if [ ! -d "$WORKDIR/initrd/var/lib" ] then [ -f "$DLDIR/pci.ids" ] || $WGET -O "$DLDIR/pci.ids" "$PCI_IDS_URL" [ -f "$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/initrd/lib/firmware" ] then [ -f "$DLDIR/nic-firmware.deb" ] || $WGET -O "$DLDIR/nic-firmware.deb" "$NIC_FIRMWARE_URL" dpkg -x "$DLDIR/nic-firmware.deb" "$WORKDIR/initrd/" find "$WORKDIR/initrd/lib/firmware/" \( -name 'ipw*' -o -name 'brcmfmac*' -o -name '*wifi*' \) -print0 | xargs -r0 rm -v fi cat > "$WORKDIR/initrd/funcs" <<"EOT" rescue_shell() { echo "Something went wrong. Dropping to a shell." setsid cttyhack sh sync umount /dev /sys /proc poweroff -d1 -f } tty_prog() { tty=/dev/tty$1; shift while true do echo "(re)spawning $@ on $tty" >$tty setsid sh -c "exec $@ <$tty >$tty 2>&1" sleep 2 done } network_up() { ip -oneline link | grep DOWN | cut -d: -f2 | grep -v sit | while read iface do echo ip link set dev $iface up ip link set dev $iface up done } machine_info() { 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 grep -F MemTotal: /proc/meminfo 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' lsusb 2>/dev/null | grep -vE hub$ | cut -d: -f2- | sed 's/^ ID/usb-device:/' } EOT cat > "$WORKDIR/initrd/init" <<"EOT" #!/bin/busybox sh echo "Initrd started" # Load helper functions . /funcs # Trace execution set -v # Setup links for programs supported by busybox /bin/busybox --install -s # Mount pseudo-filesystems mount -t proc none /proc || rescue_shell mount -t sysfs none /sys || rescue_shell mount -t devtmpfs none /dev || rescue_shell # Load keyboard map loadkmap < /etc/keys.bmap || rescue_shell # Start some debug shells (background) for i in 2 3 4 5 6; do tty_prog $i sh & done # Activate networking interfaces network_up sleep 8 # PHY link detection + IPv6 Duplicate Address Detection & Autoconf # Dump EFIvars if available efibootmgr -v # Machine basic informations machine_info # You could press Alt+F2 to have a shell. Remote control on telnet port (TCP 23) nc -ll -p 23 -e /bin/sh -i # Network IP adresses ip -o addr show | sed -ne 's/[0-9]*:\s*\(\S*\)\s*inet6*\s\(\S*\)\s.*$/\1: \2/p' EOT chmod +x "$WORKDIR/initrd/init" # Kernel build setup # ###################### kernel_tarball=$DLDIR/$(basename $KERNEL_TARBALL_URL) [ -f "$kernel_tarball" ] || $WGET -O "$kernel_tarball" "$KERNEL_TARBALL_URL" if [ ! -d "$WORKDIR/kernel" ] then mkdir "$WORKDIR/kernel" tar xf "$kernel_tarball" --strip-components=1 -C "$WORKDIR/kernel" fi if [ ! -d "$WORKDIR/kernel/scripts/Kconfiglib" ] then [ -f "$DLDIR/kconfiglib.py" ] || $WGET -O "$DLDIR/kconfiglib.py" "$KCONFIGLIB_MAIN_URL" [ -f "$DLDIR/makefile.patch" ] || $WGET -O "$DLDIR/makefile.patch" "$KCONFIGLIB_PATCH_URL" mkdir "$WORKDIR/kernel/scripts/Kconfiglib" cp "$DLDIR/kconfiglib.py" "$WORKDIR/kernel/scripts/Kconfiglib/kconfiglib.py" patch -t -p1 -d "$WORKDIR/kernel" < "$DLDIR/makefile.patch" fi cat >"$WORKDIR/kernel/scripts/Kconfiglib/customize.py" <<"EOT" #!/usr/bin/env python import kconfiglib import sys def sset(sym, value=None): if not sym.is_modifiable(): print("%s is not modifiable at all"%(sym.get_name())) return True if value is None and sym.get_type() in [ kconfiglib.BOOL, kconfiglib.TRISTATE ]: value = sym.get_upper_bound() old_value = sym.get_value() if old_value == value: return False print("CONFIG_%s=%s [was: %s]"%(sym.get_name(),value,old_value)) sym.set_user_value(value) return True conf = kconfiglib.Config(sys.argv[1]) conf.load_config('.config') support_xz = conf['KERNEL_XZ'] is not None i = 0 more_work = True while more_work and i < 10: more_work = False i += 1 print("Kconfiglib/customize.py pass %i"%i) for sym in conf.get_symbols(): name = sym.get_name() if name in ['DEFAULT_HOSTNAME']: # default is (none) and could make FreeBSD's dhcpd complain because unallowed '()' more_work = sset(sym, 'eficast') or more_work if name in ['INITRAMFS_SOURCE']: # embed initrd in the EFI bootable kernel more_work = sset(sym, '../initrd/') or more_work if name in ['EFI_STUB', 'EFI_VARS', 'DELL_RBU', 'USB_XHCI_HCD', 'IKCONFIG']: # Make kernel directly loadable by EFI, add USB3, Dell flash... more_work = sset(sym) or more_work if name in ['LOGO', 'SUSPEND', 'HIBERNATION', 'CPU_FREQ', 'PCCARD', 'HAMRADIO', 'WIRELESS', 'RFKILL', 'WLAN', 'SOUND', 'NETWORK_FILESYSTEMS', 'KEYS', 'SECURITY', 'VIRTUALIZATION']: more_work = sset(sym, 'n') or more_work if support_xz: # Compress everything with XZ if available (slower, smaller) if name in ['KERNEL_XZ']: more_work = sset(sym, 'y') or more_work if name in ['RD_GZIP', 'RD_BZIP2', 'RD_LZMA', 'RD_LZO', 'RD_LZ4']: more_work = sset(sym, 'n') or more_work # Following generic actions are meant for features, not choices if not sym.is_choice_symbol(): # Build all available net/ethernet drivers if sym.is_modifiable() and sym.get_type() in [ kconfiglib.BOOL, kconfiglib.TRISTATE ] \ and True in [ ('drivers/net/ethernet' in filename) for (filename,_) in sym.get_def_locations() ]: more_work = sset(sym) or more_work # Try to get everything in kernel, not as a module if sym.get_value() == 'm' and sym.get_upper_bound() == 'y': more_work = sset(sym, 'y') or more_work if i == 10: print("ERROR : can't set some of kernel config symbols after 10 passes") sys.exit(1) else: sys.exit( conf.write_config(".config") ) EOT chmod +x "$WORKDIR/kernel/scripts/Kconfiglib/customize.py" # Kernel build kernel # ####################### ( cd "$WORKDIR/kernel" if [ ! -f .config ] then make defconfig make scriptconfig SCRIPT=scripts/Kconfiglib/customize.py fi # Workaround : some kernel version forget to update embed initramfs in certain cases [ -f usr/initramfs_data.cpio.gz ] && rm usr/initramfs_data.cpio.gz make -j8 ) # Copy / run result EFI file # ############################## cp "$WORKDIR/kernel/arch/x86/boot/bzImage" "$OUTDIR/BOOTX64.EFI" if [ -n "$OUTUSB" -a -b "$OUTUSB" ] then [ -d "$WORKDIR/mountpoint" ] || mkdir "$WORKDIR/mountpoint" mount | grep -E "^$OUTUSB" -q && $ROOTCMD umount "$OUTUSB" $ROOTCMD mount "$OUTUSB" "$WORKDIR/mountpoint" [ -d "$WORKDIR/mountpoint/BOOT/EFI" ] || $ROOTCMD mkdir -p "$WORKDIR/mountpoint/EFI/BOOT" $ROOTCMD cp "$OUTDIR/BOOTX64.EFI" "$WORKDIR/mountpoint/EFI/BOOT" $ROOTCMD umount "$WORKDIR/mountpoint" fi [ "x$RUN_QEMU" == "xy" ] && qemu-system-x86_64 -kernel "$OUTDIR/BOOTX64.EFI"