diff --git a/meta/recipes-kernel/linux/kernel-devsrc.bb b/meta/recipes-kernel/linux/kernel-devsrc.bb
index 3b83fd145c..46337bd79b 100644
--- a/meta/recipes-kernel/linux/kernel-devsrc.bb
+++ b/meta/recipes-kernel/linux/kernel-devsrc.bb
@@ -35,9 +35,20 @@ PACKAGE_ARCH = "${MACHINE_ARCH}"
 #   source: the source needed to debug a kernel built for a specific
 #           MACHINE, see KERNELDEV_SOURCE_SPINE. No runtime dependencies,
 #           so it can go in a debug rootfs without the build tools.
+#   full:   the whole source tree (a superset of 'source').
+#   tarball: the whole source tree as /usr/src/kernel-source-<version>.tar.bz2,
+#           in place of the uncompressed tree from 'full'. Not usable by
+#           debuggers or for building, it is a copy to unpack elsewhere.
 PACKAGECONFIG ?= "build"
 PACKAGECONFIG[build] = ",,,${KERNELDEV_BUILD_RDEPENDS}"
 PACKAGECONFIG[source] = ",,"
+PACKAGECONFIG[full] = ",,"
+PACKAGECONFIG[tarball] = ",,,bzip2"
+
+# The scripts in a 'full' tree are not run from it, so unless 'build' needs
+# them, don't generate runtime dependencies from their interpreters (perl,
+# python3, make, ...).
+SKIP_FILEDEPS:${PN} = "${@'1' if bb.utils.contains('PACKAGECONFIG', 'full', True, False, d) and not bb.utils.contains_any('PACKAGECONFIG', 'build tarball', True, False, d) else '0'}"
 
 # The curated part of the 'source' level, copied in full along with
 # arch/${ARCH}. The rest (drivers, sound, samples, ...) is not curated: the
@@ -469,6 +480,45 @@ kernel_devsrc_install_source() {
     )
 }
 
+# The files of the whole source tree, NUL separated, for 'full' and
+# 'tarball'. Run from ${S}. The git metadata and kernel-meta are left out.
+kernel_devsrc_tree_files() {
+    find . -type d -name '.git*' -prune -o -type d -name '.kernel-meta' -prune -o -type f -print0
+}
+
+# The 'full' level: the whole source tree, without the git metadata.
+kernel_devsrc_install_full() {
+    kerneldir=${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}
+
+    # With 'build' also enabled, this runs after it and copies over the
+    # source files it installed (cpio -u). Most are identical, but the
+    # 'build' level's customizations are lost: scripts/package/debian/rules
+    # comes back, and the arm64 awk and python shebang fixups are undone (the
+    # awk ones are redone below). Generated files from ${B} aren't in ${S}, so
+    # they are kept. Skipping the files 'build' installed isn't worth the
+    # complexity: 'full' is meant to be the whole tree.
+    (
+        cd ${S}
+        kernel_devsrc_tree_files | cpio --quiet --null -pdlu $kerneldir/build
+    )
+
+    # The whole tree brings in scripts the 'build' level doesn't copy, and
+    # some use #!/bin/awk or #!/bin/env, which no package provides. Point them
+    # at ${bindir}, keeping their arguments (awk -f). sed -i writes a new file,
+    # so the hardlinks into ${S} are not modified.
+    grep -rlZE '^#! */bin/(awk|env)( |$)' $kerneldir/build | \
+        xargs -0 -r sed -i -E -e '1s,^#! */bin/(awk|env)( |$),#!${bindir}/\1\2,'
+}
+
+# The 'tarball' level: the whole source tree, compressed.
+kernel_devsrc_install_tarball() {
+    (
+        cd ${S}
+        kernel_devsrc_tree_files | \
+            tar --null --no-recursion -cjf ${D}/usr/src/kernel-source-${KERNEL_VERSION}.tar.bz2 --files-from -
+    )
+}
+
 do_install() {
     kerneldir=${D}${KERNEL_BUILD_ROOT}${KERNEL_VERSION}
     install -d $kerneldir
@@ -496,18 +546,25 @@ do_install() {
         kernel_devsrc_install_build
     fi
 
-    # The 'source' level goes in the same tree, at /usr/src/kernel, which is
-    # where the kernel's debug info points.
-    if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'source', '1', '', d)}" ]; then
+    # The source levels go in the same tree, at /usr/src/kernel, which is
+    # where the kernel's debug info points. 'full' is a superset of 'source',
+    # and 'tarball' replaces the uncompressed 'full' tree.
+    if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'full', '1', '', d)}" ] &&
+       [ -z "${@bb.utils.contains('PACKAGECONFIG', 'tarball', '1', '', d)}" ]; then
+        kernel_devsrc_install_full
+    elif [ -n "${@bb.utils.contains('PACKAGECONFIG', 'source', '1', '', d)}" ]; then
         kernel_devsrc_install_source
     fi
+    if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'tarball', '1', '', d)}" ]; then
+        kernel_devsrc_install_tarball
+    fi
     chown -R root:root ${D}
 }
 
 # Ensure we don't race against "make scripts" during cpio
 do_install[lockfiles] = "${TMPDIR}/kernel-scripts.lock"
 
-FILES:${PN} = "${KERNEL_BUILD_ROOT} ${KERNEL_SRC_PATH}"
+FILES:${PN} = "${KERNEL_BUILD_ROOT} ${KERNEL_SRC_PATH} /usr/src/kernel-source-*.tar.bz2"
 
 # Added to RDEPENDS:${PN} by the 'build' PACKAGECONFIG
 KERNELDEV_BUILD_RDEPENDS = "bc python3-core flex bison ${TCLIBC}-utils gawk"
@@ -521,3 +578,8 @@ KERNELDEV_BUILD_RDEPENDS += "${@bb.utils.contains('ARCH', 'powerpc', 'elfutils-d
 KERNELDEV_BUILD_RDEPENDS += "gcc-plugins libmpc-dev"
 # 5.13+ needs grep for powerpc
 KERNELDEV_BUILD_RDEPENDS:append:powerpc = " grep"
+# With the whole tree as well ('full', or 'tarball' unpacked on target), more
+# than 'make scripts prepare' can be built: a whole kernel, modules_install,
+# compressed images and modules, module signing.
+KERNELDEV_FULL_BUILD_RDEPENDS = "perl kmod cpio tar gzip xz zstd lz4 openssl"
+KERNELDEV_BUILD_RDEPENDS += "${@bb.utils.contains_any('PACKAGECONFIG', 'full tarball', '${KERNELDEV_FULL_BUILD_RDEPENDS}', '', d)}"
