diff --git a/meta/classes/uki.bbclass b/meta/classes/uki.bbclass
new file mode 100644
index 0000000000..2eff387c75
--- /dev/null
+++ b/meta/classes/uki.bbclass
@@ -0,0 +1,140 @@
+#
+# Unified kernel image (UKI) class
+#
+#
+# This bbclass is designed to repack an Overlake image as a UKI, to be booted on a qemuarm64 with SecureBoot
+# signing and embedded with TPM PCR measurements.
+#
+# The UKI is composed by:
+#   - an UEFI stub
+#     The linux kernel can generate a UEFI stub, however the one from systemd-boot can fetch
+#     the command line from a separate section of the EFI application, avoiding the need to
+#     rebuild the kernel.
+#   - the kernel
+#   - an initramfs
+#   - other metadata (e.g. PCR measurements)
+#
+#
+#
+
+# List build time dependencies
+DEPENDS += "systemd-native \
+            sbsigntool-native \
+            virtual/${TARGET_PREFIX}binutils \
+            "
+
+REQUIRED_DISTRO_FEATURES += "usrmerge systemd"
+
+inherit features_check
+require ../conf/image-uefi.conf
+
+INITRD_IMAGE ?= "core-image-minimal-initramfs"
+
+INITRD_LIVE ?= "${@ ('${DEPLOY_DIR_IMAGE}/' + d.getVar('INITRD_IMAGE') + '-${MACHINE}.cpio.gz') if d.getVar('INITRD_IMAGE') else ''}"
+
+UKI_CONFIG_FILE ?= "${WORKDIR}/core-image-minimal-uki.conf"
+UKI_FILENAME ?= "${@ 'UKI.signed.efi' if d.getVar('UKI_CONFIG_FILE') else 'UKI.unsigned.efi'}"
+
+do_uki[depends] += " \
+                        systemd-boot:do_deploy \
+                        virtual/kernel:do_deploy \
+                     "
+
+# INITRD_IMAGE is added to INITRD_LIVE, which we use to create our initrd, so depend on it if it is set
+# So we want to generate the initrd image if INITRD_IMAGE exists
+do_uki[depends] += "${@ '${INITRD_IMAGE}:do_image_complete' if d.getVar('INITRD_IMAGE') else ''}"
+
+# ensure that the build directory is empty everytime we generate a newly-created uki
+do_uki[cleandirs] = "${B}"
+# influence the build directory at the start of the builds
+do_uki[dirs] = "${B}"
+
+# we want to allow specifying files in SRC_URI, such as for signing the UKI
+python () {
+    d.delVarFlag("do_fetch","noexec")
+    d.delVarFlag("do_unpack","noexec")
+}
+
+# main task
+python do_uki() {
+    import glob
+    import subprocess
+
+    # Construct the ukify command
+    ukify_cmd = ("ukify build")
+
+    # Handle the creation of an initrd image by reading and concatenating multiple cpio files. 
+    # If the INITRD_LIVE variable is defined and not empty, it opens the necessary files, reads their contents, 
+    # and constructs a list.
+    if d.getVar('INITRD_LIVE'):
+        initrd_list = ""
+        for cpio in d.getVar('INITRD_LIVE').split():
+            # get a list of initrds
+            initrd_list += cpio + ' '
+        
+        ukify_cmd += " --initrd=%s" % initrd_list
+    else:
+        bb.fatal("ERROR - Required argument: INITRD")
+    
+    deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
+   
+    # Kernel
+    if d.getVar('KERNEL_IMAGETYPE'):
+        kernel = "%s/%s" % (deploy_dir_image, d.getVar('KERNEL_IMAGETYPE'))
+        kernel_version = d.getVar('KERNEL_VERSION')
+        if not os.path.exists(kernel):
+            bb.fatal(f"ERROR: cannot find {kernel}.")
+
+        ukify_cmd += " --linux=%s --uname %s" % (kernel, kernel_version)
+    else:
+        bb.fatal("ERROR - Required argument: KERNEL")
+
+    # Architecture
+    target_arch = d.getVar('EFI_ARCH')
+    ukify_cmd += " --efi-arch %s" % target_arch
+
+    # Stub
+    stub = "%s/linux%s.efi.stub" % (deploy_dir_image, target_arch)
+    if not os.path.exists(stub):
+        bb.fatal(f"ERROR: cannot find {stub}.")
+    ukify_cmd += " --stub %s" % stub
+
+    # Add option for dtb
+    if d.getVar('KERNEL_DEVICETREE'):
+        first_dtb = d.getVar('KERNEL_DEVICETREE').split()[0]
+        dtb_path = "%s/%s" % (deploy_dir_image, first_dtb)
+
+        if not os.path.exists(dtb_path):
+            bb.fatal(f"ERROR: cannot find {dtb_path}.")
+
+        ukify_cmd += " --devicetree %s" % dtb_path
+
+    # Add option to pass a config file to sign the UKI.
+    if os.path.exists(d.getVar('UKI_CONFIG_FILE')):
+        ukify_cmd += " --config=%s" % d.getVar('UKI_CONFIG_FILE')
+        ukify_cmd += " --tools=%s%s/lib/systemd/tools" % (d.getVar("RECIPE_SYSROOT_NATIVE"), d.getVar("prefix"))
+        bb.note("Pulling keys from config file")
+    else: 
+        bb.note("Generating unsigned UKI")
+
+    # Custom UKI name
+    output = " --output=%s" % d.getVar('UKI_FILENAME')
+    ukify_cmd += " %s" % output
+
+    # Set env to determine where bitbake should look for dynamic libraries
+    env = os.environ.copy() # get the env variables
+    env['LD_LIBRARY_PATH'] = d.expand("${RECIPE_SYSROOT_NATIVE}/usr/lib/systemd:${LD_LIBRARY_PATH}")
+    
+    # Run the ukify command
+    subprocess.check_call(ukify_cmd, env=env, shell=True)
+}
+ 
+inherit deploy
+
+do_deploy () {
+    # Copy generated UKI into DEPLOYDIR
+	install ${B}/${UKI_FILENAME} ${DEPLOYDIR}
+}
+
+addtask uki before do_deploy do_image after do_rootfs
+addtask deploy before do_build after do_compile
\ No newline at end of file
diff --git a/meta/recipes-core/systemd/systemd_254.bb b/meta/recipes-core/systemd/systemd_254.bb
index 8d5cf13095..65f132abb8 100644
--- a/meta/recipes-core/systemd/systemd_254.bb
+++ b/meta/recipes-core/systemd/systemd_254.bb
@@ -6,6 +6,9 @@ PE = "1"
 
 DEPENDS = "intltool-native gperf-native libcap util-linux python3-jinja2-native"
 
+# The Ukify tool requires this module
+DEPENDS:append:class-native = " python3-pefile-native"
+
 SECTION = "base/shell"
 
 inherit useradd pkgconfig meson perlnative update-rc.d update-alternatives qemu systemd gettext bash-completion manpages features_check
@@ -18,6 +21,8 @@ REQUIRED_DISTRO_FEATURES += "usrmerge"
 # that we don't build both udev and systemd in world builds.
 REQUIRED_DISTRO_FEATURES += "systemd"
 
+REQUIRED_DISTRO_FEATURES:class-native = ""
+
 SRC_URI += " \
            file://touchscreen.rules \
            file://00-create-volatile.conf \
@@ -120,6 +125,8 @@ PACKAGECONFIG:remove:libc-musl = " \
 # https://github.com/seccomp/libseccomp/issues/347
 PACKAGECONFIG:remove:mipsarch = "seccomp"
 
+PACKAGECONFIG:class-native = "serial-getty-generator openssl tpm2 efi"
+
 TARGET_CC_ARCH:append:libc-musl = " -D__UAPI_DEF_ETHHDR=0 -D_LARGEFILE64_SOURCE"
 
 # Some of the dependencies are weak-style recommends - if not available at runtime,
@@ -260,6 +267,9 @@ EXTRA_OEMESON += "-Dkexec-path=${sbindir}/kexec \
                   -Dloadkeys-path=${bindir}/loadkeys \
                   -Dsetfont-path=${bindir}/setfont"
 
+EXTRA_OEMESON:append:class-native = " -Dbootloader=true \
+                                      -Dman=false \
+                                    "
 # The 60 seconds is watchdog's default vaule.
 WATCHDOG_TIMEOUT ??= "60"
 
@@ -380,6 +390,14 @@ do_install() {
     fi
 }
 
+do_install:class-native() {
+	meson_do_install
+	install -d ${D}${bindir}
+	install -m 0755 ${S}/src/ukify/ukify.py ${D}${bindir}/ukify
+	install -d ${D}${prefix}/lib/systemd/tools
+	install -m 0755 ${B}/systemd-measure ${D}${prefix}/lib/systemd/tools
+}
+
 python populate_packages:prepend (){
     systemdlibdir = d.getVar("rootlibdir")
     do_split_packages(d, systemdlibdir, r'^lib(.*)\.so\.*', 'lib%s', 'Systemd %s library', extra_depends='', allow_links=True)
@@ -702,6 +720,9 @@ RRECOMMENDS:${PN} += "systemd-extra-utils \
                       ${@bb.utils.contains('PACKAGECONFIG', 'logind', 'pam-plugin-umask', '', d)} \
 "
 
+RRECOMMENDS:${PN}:class-native = ""
+RDEPENDS:${PN}:class-native = ""
+
 INSANE_SKIP:${PN} += "dev-so libdir"
 INSANE_SKIP:${PN}-dbg += "libdir"
 INSANE_SKIP:${PN}-doc += " libdir"
@@ -852,3 +873,5 @@ pkg_postinst:udev-hwdb () {
 pkg_prerm:udev-hwdb () {
 	rm -f $D${sysconfdir}/udev/hwdb.bin
 }
+
+BBCLASSEXTEND += "native"
