github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/tests/lib/external/snapd-testing-tools/tools/repack-kernel (about)

     1  #!/bin/bash
     2  set -e
     3  
     4  if [ -n "$D" ]; then
     5      set -x
     6  fi
     7  
     8  show_help() {
     9      echo "usage: repack-kernel <command> <opts>"
    10      echo ""
    11      echo "handle extraction of the kernel snap to a workspace directory, and later"
    12      echo "repacking it back to a snap."
    13      echo ""
    14      echo "   repack-kernel setup                        - setup system dependencies"
    15      echo "   repack-kernel extract <snap-file> <target> - extract under <target> workspace tree"
    16      echo "   repack-kernel prepare <target>             - prepare initramfs & kernel for repacking"
    17      echo "   repack-kernel pack <target>                - pack the kernel"
    18      echo "   repack-kernel cull-firmware <target>       - remove unnecessary firmware"
    19      echo "   repack-kernel cull-modules <target>        - remove unnecessary modules"
    20      echo ""
    21  }
    22  
    23  setup() {
    24      if [ "$UID" != "0" ]; then
    25          echo "run as root (only this command)"
    26          exit 1
    27      fi
    28  
    29      # carries ubuntu-core-initframfs
    30      add-apt-repository ppa:snappy-dev/image -y
    31      apt update
    32      
    33      if ! dpkg -s ubuntu-core-initramfs >/dev/null 2>&1; then
    34          if ! apt install -y ubuntu-core-initramfs; then
    35              echo "ubuntu-core-initramfs was not available in deb repository!"
    36              exit 1
    37          fi
    38      fi
    39  }
    40  
    41  get_kver() {
    42      local kerneldir="$1"
    43      find "$kerneldir" -maxdepth 1 -name "config-*" | grep -Po 'config-\K.*'
    44  }
    45  
    46  extract() {
    47      local snap_file="$1"
    48      local target="$2"
    49  
    50      if [ -z "$target" ] || [ -z "$snap_file" ]; then
    51          echo "repack-kernel: invalid arguments for extract"
    52          exit 1
    53      fi
    54  
    55      target=$(realpath "$target")
    56  
    57      mkdir -p "$target" "$target/work" "$target/backup"
    58  
    59      # kernel snap is huge, unpacking to current dir
    60      unsquashfs -d "$target/kernel" "$snap_file"
    61  
    62      kver="$(get_kver "$target/kernel")"
    63  
    64      # repack initrd magic, beware
    65      # assumptions: initrd is compressed with LZ4, cpio block size 512, microcode
    66      # at the beginning of initrd image
    67  
    68      # XXX: ideally we should unpack the initrd, replace snap-boostrap and
    69      # repack it using ubuntu-core-initramfs --skeleton=<unpacked> this does not
    70      # work and the rebuilt kernel.efi panics unable to start init, but we
    71      # still need the unpacked initrd to get the right kernel modules
    72      objcopy -j .initrd -O binary "$target"/kernel/kernel.efi "$target/work/initrd"
    73  
    74      # copy out the kernel image for create-efi command
    75      objcopy -j .linux -O binary "$target"/kernel/kernel.efi "$target/work/vmlinuz-$kver"
    76  
    77      cp -a "$target"/kernel/kernel.efi "$target/backup/"
    78  
    79      # this works on 20.04 but not on 18.04
    80      unmkinitramfs "$target"/work/initrd "$target"/work/unpacked-initrd
    81      
    82      # copy the unpacked initrd to use as the target skeleton
    83      cp -ar "$target/work/unpacked-initrd" "$target/skeleton"
    84  
    85      echo "prepared workspace at $target"
    86      echo "  kernel:              $target/kernel ($kver)"
    87      echo "  kernel.efi backup:   $target/backup/kernel.efi"
    88      echo "  temporary artifacts: $target/work"
    89      echo "  initramfs skeleton:  $target/skeleton"
    90  
    91  }
    92  
    93  prepare() {
    94      local target="$1"
    95  
    96      if [ -z "$target" ]; then
    97          echo "repack-kernel: missing target for prepare"
    98          exit 1
    99      fi
   100  
   101      target=$(realpath "$target")
   102      
   103      local kver
   104      kver="$(get_kver "$target/kernel")"
   105  
   106      (
   107          # all the skeleton edits go to a local copy of distro directory
   108          local skeletondir="$target/skeleton"
   109  
   110          cd "$target/work"
   111          # XXX: need to be careful to build an initrd using the right kernel
   112          # modules from the unpacked initrd, rather than the host which may be
   113          # running a different kernel
   114          (
   115              # accommodate assumptions about tree layout, use the unpacked initrd
   116              # to pick up the right modules
   117              cd unpacked-initrd/main
   118              ubuntu-core-initramfs create-initrd \
   119                                    --kernelver "$kver" \
   120                                    --skeleton "$skeletondir" \
   121                                    --feature main \
   122                                    --kerneldir "lib/modules/$kver" \
   123                                    --output "$target/work/repacked-initrd"
   124          )
   125  
   126          # assumes all files are named <name>-$kver
   127          ubuntu-core-initramfs create-efi \
   128                                --kernelver "$kver" \
   129                                --initrd repacked-initrd \
   130                                --kernel vmlinuz \
   131                                --output repacked-kernel.efi
   132  
   133          cp "repacked-kernel.efi-$kver" "$target/kernel/kernel.efi"
   134  
   135          # XXX: needed?
   136          chmod +x "$target/kernel/kernel.efi"
   137      )
   138  }
   139  
   140  cull_firmware() {
   141      local target="$1"
   142  
   143      if [ -z "$target" ]; then
   144          echo "repack-kernel: missing target for cull-firmware"
   145          exit 1
   146      fi
   147      
   148      # XXX: drop ~450MB+ of firmware which should not be needed in under qemu
   149      # or the cloud system
   150      rm -rf "$target"/kernel/firmware/*
   151  }
   152  
   153  cull_modules() {
   154      local target="$1"
   155  
   156      if [ -z "$target" ]; then
   157          echo "repack-kernel: missing target for cull-modules"
   158          exit 1
   159      fi
   160  
   161      target=$(realpath "$target")
   162      
   163      local kver
   164      kver="$(get_kver "$target/kernel")"
   165  
   166      (
   167          cd "$target/kernel"
   168          # drop unnecessary modules
   169          awk '{print $1}' <  /proc/modules  | sort > "$target/work/current-modules"
   170          #shellcheck disable=SC2044
   171          for m in $(find modules/ -name '*.ko'); do
   172              noko=$(basename "$m"); noko="${noko%.ko}"
   173              if echo "$noko" | grep -f "$target/work/current-modules" -q ; then
   174                  echo "keeping $m - $noko"
   175              else
   176                  rm -f "$m"
   177              fi
   178          done
   179  
   180          # depmod assumes that /lib/modules/$kver is under basepath
   181          mkdir -p fake/lib
   182          ln -s "$PWD/modules" fake/lib/modules
   183          depmod -b "$PWD/fake" -A -v "$kver"
   184          rm -rf fake
   185      )
   186  }
   187  
   188  pack() {
   189      local target="$1"
   190  
   191      if [ -z "$target" ]; then
   192          echo "repack-kernel: missing target for pack"
   193          exit 1
   194      fi
   195      snap pack "${@:2}" "$target/kernel" 
   196  }
   197  
   198  main() {
   199      if [ $# -eq 0 ]; then
   200          show_help
   201          exit 0
   202      fi
   203  
   204      local subcommand="$1"
   205      local action=
   206      while [ $# -gt 0 ]; do
   207          case "$1" in
   208              -h|--help)
   209                  show_help
   210                  exit 0
   211                  ;;
   212              *)
   213                  action=$(echo "$subcommand" | tr '-' '_')
   214                  shift
   215                  break
   216                  ;;
   217          esac
   218      done
   219  
   220      if [ -z "$(declare -f "$action")" ]; then
   221          echo "repack-kernel: no such command: $subcommand" >&2
   222          show_help
   223          exit 1
   224      fi
   225  
   226      "$action" "$@"
   227  }
   228  
   229  main "$@"
   230