From nobody Mon Oct 6 08:26:54 2025 Received: from mail-yw1-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE4EF2F85FF for ; Wed, 23 Jul 2025 14:47:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753282029; cv=none; b=qm2dF8gZOtg29Q7kOQ/Fp3JMYthNEs7tMaUX7N3dlZ+JA+jhDgx+V5yABLd+XmQ4xhDJGZgXDjnH31VzmP/5kklBO9FlYhtauLa+X0ix6SsvEVloU33MznI3w01mMWIbZKlPzDMi1zc+7UARkjF5mFEGCU+Llbj4qKJFfycaib8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753282029; c=relaxed/simple; bh=QKiIrXzuD4XfVxFEvjmQqJZ6VbxVWuGjRJ+RtdblcSs=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=D4OXxMmxOfZp/LQB+MbzU5lp+FmhkSEDe2QMlWX2eXB+J280DA+Z2mcs2fnoofmkjHVUhzzz9TIn03D/LU2471vD89jCp5exp2IEX7UAfQXS5bfWljKzZsM+MZF3uKmUSNaUTdyLgE/joVDkQW3svAy9K/Idlow7bWjM3iBn3UI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen-com.20230601.gappssmtp.com header.i=@soleen-com.20230601.gappssmtp.com header.b=cLdUHu1/; arc=none smtp.client-ip=209.85.128.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen-com.20230601.gappssmtp.com header.i=@soleen-com.20230601.gappssmtp.com header.b="cLdUHu1/" Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-70e5e6ab7b8so58491587b3.1 for ; Wed, 23 Jul 2025 07:47:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen-com.20230601.gappssmtp.com; s=20230601; t=1753282025; x=1753886825; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=wO2wIgP85BCTULTLfa+o1uX+1i01pv/IFr2OKVLnU4k=; b=cLdUHu1/eAuppDYWiBrTeyu/D3n6CkkiXBP5odzGT9NAaJuklHIYJcey7dnTfnTE9B gBrZE2ysZ9574YODASQUXIUkXdIE2g7ukqcMMh4FDsVN4VlWQ4Kkv87sy9bDyDSI3vMJ FGtESQOvRsBWAMpm0t3UrYgzhDWV+YwmWkwdE4v8jgLBzI28nJSyy7FaMItJbDyD1O3r 1F0zLWgoPe4csLFJ61le94aHfRwZaEvqccZIpraRioHBxDsnVpNUfJwPnyHHbv8XnbnG 32zdQ8VO2J675BhoYy8DZBBD40yS+fx1B4o99IGb2uB+sPSBs9ucTtvWYK3yZMkG6PK4 2yrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1753282025; x=1753886825; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wO2wIgP85BCTULTLfa+o1uX+1i01pv/IFr2OKVLnU4k=; b=LuLvpeGAeZLBO5GJHTz9ByoXJrdwsIHjLS06li7cAnKuz28n5YXWeUEPUw5fKVejd7 q2btCG1XqjgqVRjxjEQ3G3Bet126/QyMfJ5PHynoafdmtTrflO6E890HonN+QcUmEWke tOcznPzqIc9nTM3f5X59PIsTFL0FackcgUvvI67VUVBII28dBtPdHrU0coJpNdvJuHdp oDsFpNNMZxMQId2j4/zQExwYeV7rc2Un6rleXwb7VRk6KimY3/X9GV+hyOsAbomSYwng gKHVGIjpFcPdlnVOjFyX6t83WIB7nHUvRbLuq4h7oMaX4hxWS8+gpE5/5SCRb+D449Us Z/9g== X-Forwarded-Encrypted: i=1; AJvYcCXE9WiPw4cmouxbF/cy52QWgTu/vQZVAkexPip5jm0zPVVawCBgUV9LuwIHnbi35473u1itxUJiIF3zy8I=@vger.kernel.org X-Gm-Message-State: AOJu0YxDLMv25L401XPVpgDm7hkXD+Dh5+UK9jOpAAb/Lf/GUh/Juow/ eVF7JnPwaomLezD2HkZnwAGbToNIYh6vfRlfdNmxgsFEhNfE9b252CppV68cb+tdp+Q= X-Gm-Gg: ASbGncssFwiNRlsClUkz9lcYZlh9gXrafX87ivw21Hi755gGmQlfxPrFGw9urIu3yZs PK15LcPm9c5cftcat1JgpX4eDTsfAcC663wIgbbljq0N3aPr/R4BXCwVOkvWDA2AmzaQDT9159o nMy3rn6DIWvC9uytuwdPqMt61OrTjCuMRqeIgTjFDoeHL3TZHW3UrNtAN8aLD6EJLaqUDTwBY6O tMF+XpSdfpEkUTuNFtbtfdvh7egvCC1LAeGrsIOQ2jhP5K9eJBPQiP4baGAENNxEyUFwXesPyei be+7onvCQAyMPiJCmosF+0YCKCFmLzlbCDf+VfFf3uVVdo6giOVR+M8iKVxBL8wf0ZlIapgrZ3f 2E6wFpmUlMBriue+SMgN/9ElXrw7658LEPIDy0yevd9MLJDjEpWtg2qJKT0T8+3IL/yPlWTJNgS 1NDI3kZK21KRb/NUEATVLYgkdj X-Google-Smtp-Source: AGHT+IE6rL13P8SmxsZ2hnJI/80dcvjlLdQGko+A8D2Jc2ZZD8DIngXktvGTpmE5ph+kb1Vpkot2Qg== X-Received: by 2002:a05:690c:6803:b0:718:17b1:3eb with SMTP id 00721157ae682-719b41b9f44mr43595217b3.8.1753282024457; Wed, 23 Jul 2025 07:47:04 -0700 (PDT) Received: from soleen.c.googlers.com.com (235.247.85.34.bc.googleusercontent.com. [34.85.247.235]) by smtp.gmail.com with ESMTPSA id 00721157ae682-719532c7e4fsm30482117b3.72.2025.07.23.07.47.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Jul 2025 07:47:03 -0700 (PDT) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.com, changyuanl@google.com, pasha.tatashin@soleen.com, rppt@kernel.org, dmatlack@google.com, rientjes@google.com, corbet@lwn.net, rdunlap@infradead.org, ilpo.jarvinen@linux.intel.com, kanie@linux.alibaba.com, ojeda@kernel.org, aliceryhl@google.com, masahiroy@kernel.org, akpm@linux-foundation.org, tj@kernel.org, yoann.congal@smile.fr, mmaurer@google.com, roman.gushchin@linux.dev, chenridong@huawei.com, axboe@kernel.dk, mark.rutland@arm.com, jannh@google.com, vincent.guittot@linaro.org, hannes@cmpxchg.org, dan.j.williams@intel.com, david@redhat.com, joel.granados@kernel.org, rostedt@goodmis.org, anna.schumaker@oracle.com, song@kernel.org, zhangguopeng@kylinos.cn, linux@weissschuh.net, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, gregkh@linuxfoundation.org, tglx@linutronix.de, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com, rafael@kernel.org, dakr@kernel.org, bartosz.golaszewski@linaro.org, cw00.choi@samsung.com, myungjoo.ham@samsung.com, yesanishhere@gmail.com, Jonathan.Cameron@huawei.com, quic_zijuhu@quicinc.com, aleksander.lobakin@intel.com, ira.weiny@intel.com, andriy.shevchenko@linux.intel.com, leon@kernel.org, lukas@wunner.de, bhelgaas@google.com, wagi@kernel.org, djeffery@redhat.com, stuart.w.hayes@gmail.com, ptyadav@amazon.de, lennart@poettering.net, brauner@kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, saeedm@nvidia.com, ajayachandra@nvidia.com, jgg@nvidia.com, parav@nvidia.com, leonro@nvidia.com, witu@nvidia.com Subject: [PATCH v2 05/32] kho: make debugfs interface optional Date: Wed, 23 Jul 2025 14:46:18 +0000 Message-ID: <20250723144649.1696299-6-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.50.0.727.gbf7dc18ff4-goog In-Reply-To: <20250723144649.1696299-1-pasha.tatashin@soleen.com> References: <20250723144649.1696299-1-pasha.tatashin@soleen.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently, KHO is controlled via debugfs interface, but once LUO is introduced, it can control KHO, and the debug interface becomes optional. Add a separate config CONFIG_KEXEC_HANDOVER_DEBUG that enables the debugfs interface, and allows to inspect the tree. Move all debugfs related code to a new file to keep the .c files clear of ifdefs. Signed-off-by: Pasha Tatashin Co-developed-by: Mike Rapoport (Microsoft) Signed-off-by: Mike Rapoport (Microsoft) --- MAINTAINERS | 3 +- kernel/Kconfig.kexec | 10 ++ kernel/Makefile | 1 + kernel/kexec_handover.c | 278 ++++--------------------------- kernel/kexec_handover_debug.c | 218 ++++++++++++++++++++++++ kernel/kexec_handover_internal.h | 44 +++++ 6 files changed, 311 insertions(+), 243 deletions(-) create mode 100644 kernel/kexec_handover_debug.c create mode 100644 kernel/kexec_handover_internal.h diff --git a/MAINTAINERS b/MAINTAINERS index 10850512c118..00de7c78de86 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13349,13 +13349,14 @@ KEXEC HANDOVER (KHO) M: Alexander Graf M: Mike Rapoport M: Changyuan Lyu +M: Pasha Tatashin L: kexec@lists.infradead.org L: linux-mm@kvack.org S: Maintained F: Documentation/admin-guide/mm/kho.rst F: Documentation/core-api/kho/* F: include/linux/kexec_handover.h -F: kernel/kexec_handover.c +F: kernel/kexec_handover* =20 KEYS-ENCRYPTED M: Mimi Zohar diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec index 1224dd937df0..9968d3d4dd17 100644 --- a/kernel/Kconfig.kexec +++ b/kernel/Kconfig.kexec @@ -109,6 +109,16 @@ config KEXEC_HANDOVER to keep data or state alive across the kexec. For this to work, both source and target kernels need to have this option enabled. =20 +config KEXEC_HANDOVER_DEBUG + bool "kexec handover debug interface" + depends on KEXEC_HANDOVER + depends on DEBUG_FS + help + Allow to control kexec handover device tree via debugfs + interface, i.e. finalize the state or aborting the finalization. + Also, enables inspecting the KHO fdt trees with the debugfs binary + blobs. + config CRASH_DUMP bool "kernel crash dumps" default ARCH_DEFAULT_CRASH_DUMP diff --git a/kernel/Makefile b/kernel/Makefile index 32e80dd626af..e4b4afa86a70 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_KEXEC) +=3D kexec.o obj-$(CONFIG_KEXEC_FILE) +=3D kexec_file.o obj-$(CONFIG_KEXEC_ELF) +=3D kexec_elf.o obj-$(CONFIG_KEXEC_HANDOVER) +=3D kexec_handover.o +obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) +=3D kexec_handover_debug.o obj-$(CONFIG_BACKTRACE_SELF_TEST) +=3D backtracetest.o obj-$(CONFIG_COMPAT) +=3D compat.o obj-$(CONFIG_CGROUPS) +=3D cgroup/ diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c index c6ccc8e0705d..32fdc388752b 100644 --- a/kernel/kexec_handover.c +++ b/kernel/kexec_handover.c @@ -10,7 +10,6 @@ =20 #include #include -#include #include #include #include @@ -27,6 +26,7 @@ */ #include "../mm/internal.h" #include "kexec_internal.h" +#include "kexec_handover_internal.h" =20 #define KHO_FDT_COMPATIBLE "kho-v1" #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map" @@ -84,8 +84,6 @@ struct khoser_mem_chunk; =20 struct kho_serialization { struct page *fdt; - struct list_head fdt_list; - struct dentry *sub_fdt_dir; struct kho_mem_track track; /* First chunk of serialized preserved memory map */ struct khoser_mem_chunk *preserved_mem_map; @@ -381,8 +379,8 @@ static void __init kho_mem_deserialize(const void *fdt) * area for early allocations that happen before page allocator is * initialized. */ -static struct kho_scratch *kho_scratch; -static unsigned int kho_scratch_cnt; +struct kho_scratch *kho_scratch; +unsigned int kho_scratch_cnt; =20 /* * The scratch areas are scaled by default as percent of memory allocated = from @@ -569,36 +567,24 @@ static void __init kho_reserve_scratch(void) kho_enable =3D false; } =20 -struct fdt_debugfs { - struct list_head list; - struct debugfs_blob_wrapper wrapper; - struct dentry *file; +struct kho_out { + struct blocking_notifier_head chain_head; + struct mutex lock; /* protects KHO FDT finalization */ + struct kho_serialization ser; + bool finalized; + struct kho_debugfs dbg; }; =20 -static int kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, - const char *name, const void *fdt) -{ - struct fdt_debugfs *f; - struct dentry *file; - - f =3D kmalloc(sizeof(*f), GFP_KERNEL); - if (!f) - return -ENOMEM; - - f->wrapper.data =3D (void *)fdt; - f->wrapper.size =3D fdt_totalsize(fdt); - - file =3D debugfs_create_blob(name, 0400, dir, &f->wrapper); - if (IS_ERR(file)) { - kfree(f); - return PTR_ERR(file); - } - - f->file =3D file; - list_add(&f->list, list); - - return 0; -} +static struct kho_out kho_out =3D { + .chain_head =3D BLOCKING_NOTIFIER_INIT(kho_out.chain_head), + .lock =3D __MUTEX_INITIALIZER(kho_out.lock), + .ser =3D { + .track =3D { + .orders =3D XARRAY_INIT(kho_out.ser.track.orders, 0), + }, + }, + .finalized =3D false, +}; =20 /** * kho_add_subtree - record the physical address of a sub FDT in KHO root = tree. @@ -611,7 +597,8 @@ static int kho_debugfs_fdt_add(struct list_head *list, = struct dentry *dir, * by KHO for the new kernel to retrieve it after kexec. * * A debugfs blob entry is also created at - * ``/sys/kernel/debug/kho/out/sub_fdts/@name``. + * ``/sys/kernel/debug/kho/out/sub_fdts/@name`` when kernel is configured = with + * CONFIG_KEXEC_HANDOVER_DEBUG * * Return: 0 on success, error code on failure */ @@ -628,33 +615,10 @@ int kho_add_subtree(struct kho_serialization *ser, co= nst char *name, void *fdt) if (err) return err; =20 - return kho_debugfs_fdt_add(&ser->fdt_list, ser->sub_fdt_dir, name, fdt); + return kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false); } EXPORT_SYMBOL_GPL(kho_add_subtree); =20 -struct kho_out { - struct blocking_notifier_head chain_head; - - struct dentry *dir; - - struct mutex lock; /* protects KHO FDT finalization */ - - struct kho_serialization ser; - bool finalized; -}; - -static struct kho_out kho_out =3D { - .chain_head =3D BLOCKING_NOTIFIER_INIT(kho_out.chain_head), - .lock =3D __MUTEX_INITIALIZER(kho_out.lock), - .ser =3D { - .fdt_list =3D LIST_HEAD_INIT(kho_out.ser.fdt_list), - .track =3D { - .orders =3D XARRAY_INIT(kho_out.ser.track.orders, 0), - }, - }, - .finalized =3D false, -}; - int register_kho_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&kho_out.chain_head, nb); @@ -734,29 +698,6 @@ int kho_preserve_phys(phys_addr_t phys, size_t size) } EXPORT_SYMBOL_GPL(kho_preserve_phys); =20 -/* Handling for debug/kho/out */ - -static struct dentry *debugfs_root; - -static int kho_out_update_debugfs_fdt(void) -{ - int err =3D 0; - struct fdt_debugfs *ff, *tmp; - - if (kho_out.finalized) { - err =3D kho_debugfs_fdt_add(&kho_out.ser.fdt_list, kho_out.dir, - "fdt", page_to_virt(kho_out.ser.fdt)); - } else { - list_for_each_entry_safe(ff, tmp, &kho_out.ser.fdt_list, list) { - debugfs_remove(ff->file); - list_del(&ff->list); - kfree(ff); - } - } - - return err; -} - static int __kho_abort(void) { int err; @@ -809,7 +750,8 @@ int kho_abort(void) goto unlock; =20 kho_out.finalized =3D false; - ret =3D kho_out_update_debugfs_fdt(); + + kho_debugfs_cleanup(&kho_out.dbg); =20 unlock: mutex_unlock(&kho_out.lock); @@ -860,7 +802,7 @@ static int __kho_finalize(void) abort: if (err) { pr_err("Failed to convert KHO state tree: %d\n", err); - kho_abort(); + __kho_abort(); } =20 return err; @@ -885,7 +827,8 @@ int kho_finalize(void) goto unlock; =20 kho_out.finalized =3D true; - ret =3D kho_out_update_debugfs_fdt(); + ret =3D kho_debugfs_fdt_add(&kho_out.dbg, "fdt", + page_to_virt(kho_out.ser.fdt), true); =20 unlock: mutex_unlock(&kho_out.lock); @@ -893,112 +836,24 @@ int kho_finalize(void) } EXPORT_SYMBOL_GPL(kho_finalize); =20 -static int kho_out_finalize_get(void *data, u64 *val) +bool kho_finalized(void) { - mutex_lock(&kho_out.lock); - *val =3D kho_out.finalized; - mutex_unlock(&kho_out.lock); - - return 0; -} - -static int kho_out_finalize_set(void *data, u64 _val) -{ - int ret =3D 0; - bool val =3D !!_val; + bool ret; =20 mutex_lock(&kho_out.lock); - - if (val =3D=3D kho_out.finalized) { - if (kho_out.finalized) - ret =3D -EEXIST; - else - ret =3D -ENOENT; - goto unlock; - } - - if (val) - ret =3D kho_finalize(); - else - ret =3D kho_abort(); - - if (ret) - goto unlock; - - kho_out.finalized =3D val; - ret =3D kho_out_update_debugfs_fdt(); - -unlock: + ret =3D kho_out.finalized; mutex_unlock(&kho_out.lock); - return ret; -} - -DEFINE_DEBUGFS_ATTRIBUTE(fops_kho_out_finalize, kho_out_finalize_get, - kho_out_finalize_set, "%llu\n"); - -static int scratch_phys_show(struct seq_file *m, void *v) -{ - for (int i =3D 0; i < kho_scratch_cnt; i++) - seq_printf(m, "0x%llx\n", kho_scratch[i].addr); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(scratch_phys); - -static int scratch_len_show(struct seq_file *m, void *v) -{ - for (int i =3D 0; i < kho_scratch_cnt; i++) - seq_printf(m, "0x%llx\n", kho_scratch[i].size); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(scratch_len); - -static __init int kho_out_debugfs_init(void) -{ - struct dentry *dir, *f, *sub_fdt_dir; - - dir =3D debugfs_create_dir("out", debugfs_root); - if (IS_ERR(dir)) - return -ENOMEM; - - sub_fdt_dir =3D debugfs_create_dir("sub_fdts", dir); - if (IS_ERR(sub_fdt_dir)) - goto err_rmdir; =20 - f =3D debugfs_create_file("scratch_phys", 0400, dir, NULL, - &scratch_phys_fops); - if (IS_ERR(f)) - goto err_rmdir; - - f =3D debugfs_create_file("scratch_len", 0400, dir, NULL, - &scratch_len_fops); - if (IS_ERR(f)) - goto err_rmdir; - - f =3D debugfs_create_file("finalize", 0600, dir, NULL, - &fops_kho_out_finalize); - if (IS_ERR(f)) - goto err_rmdir; - - kho_out.dir =3D dir; - kho_out.ser.sub_fdt_dir =3D sub_fdt_dir; - return 0; - -err_rmdir: - debugfs_remove_recursive(dir); - return -ENOENT; + return ret; } =20 struct kho_in { - struct dentry *dir; phys_addr_t fdt_phys; phys_addr_t scratch_phys; - struct list_head fdt_list; + struct kho_debugfs dbg; }; =20 static struct kho_in kho_in =3D { - .fdt_list =3D LIST_HEAD_INIT(kho_in.fdt_list), }; =20 static const void *kho_get_fdt(void) @@ -1042,56 +897,6 @@ int kho_retrieve_subtree(const char *name, phys_addr_= t *phys) } EXPORT_SYMBOL_GPL(kho_retrieve_subtree); =20 -/* Handling for debugfs/kho/in */ - -static __init int kho_in_debugfs_init(const void *fdt) -{ - struct dentry *sub_fdt_dir; - int err, child; - - kho_in.dir =3D debugfs_create_dir("in", debugfs_root); - if (IS_ERR(kho_in.dir)) - return PTR_ERR(kho_in.dir); - - sub_fdt_dir =3D debugfs_create_dir("sub_fdts", kho_in.dir); - if (IS_ERR(sub_fdt_dir)) { - err =3D PTR_ERR(sub_fdt_dir); - goto err_rmdir; - } - - err =3D kho_debugfs_fdt_add(&kho_in.fdt_list, kho_in.dir, "fdt", fdt); - if (err) - goto err_rmdir; - - fdt_for_each_subnode(child, fdt, 0) { - int len =3D 0; - const char *name =3D fdt_get_name(fdt, child, NULL); - const u64 *fdt_phys; - - fdt_phys =3D fdt_getprop(fdt, child, "fdt", &len); - if (!fdt_phys) - continue; - if (len !=3D sizeof(*fdt_phys)) { - pr_warn("node `%s`'s prop `fdt` has invalid length: %d\n", - name, len); - continue; - } - err =3D kho_debugfs_fdt_add(&kho_in.fdt_list, sub_fdt_dir, name, - phys_to_virt(*fdt_phys)); - if (err) { - pr_warn("failed to add fdt `%s` to debugfs: %d\n", name, - err); - continue; - } - } - - return 0; - -err_rmdir: - debugfs_remove_recursive(kho_in.dir); - return err; -} - static __init int kho_init(void) { int err =3D 0; @@ -1106,27 +911,16 @@ static __init int kho_init(void) goto err_free_scratch; } =20 - debugfs_root =3D debugfs_create_dir("kho", NULL); - if (IS_ERR(debugfs_root)) { - err =3D -ENOENT; + err =3D kho_debugfs_init(); + if (err) goto err_free_fdt; - } =20 - err =3D kho_out_debugfs_init(); + err =3D kho_out_debugfs_init(&kho_out.dbg); if (err) goto err_free_fdt; =20 if (fdt) { - err =3D kho_in_debugfs_init(fdt); - /* - * Failure to create /sys/kernel/debug/kho/in does not prevent - * reviving state from KHO and setting up KHO for the next - * kexec. - */ - if (err) - pr_err("failed exposing handover FDT in debugfs: %d\n", - err); - + kho_in_debugfs_init(&kho_in.dbg, fdt); return 0; } =20 diff --git a/kernel/kexec_handover_debug.c b/kernel/kexec_handover_debug.c new file mode 100644 index 000000000000..b88d138a97be --- /dev/null +++ b/kernel/kexec_handover_debug.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * kexec_handover.c - kexec handover metadata processing + * Copyright (C) 2023 Alexander Graf + * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport + * Copyright (C) 2025 Google LLC, Changyuan Lyu + * Copyright (C) 2025 Google LLC, Pasha Tatashin + */ + +#define pr_fmt(fmt) "KHO: " fmt + +#include +#include +#include +#include +#include "kexec_handover_internal.h" + +static struct dentry *debugfs_root; + +struct fdt_debugfs { + struct list_head list; + struct debugfs_blob_wrapper wrapper; + struct dentry *file; +}; + +static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *di= r, + const char *name, const void *fdt) +{ + struct fdt_debugfs *f; + struct dentry *file; + + f =3D kmalloc(sizeof(*f), GFP_KERNEL); + if (!f) + return -ENOMEM; + + f->wrapper.data =3D (void *)fdt; + f->wrapper.size =3D fdt_totalsize(fdt); + + file =3D debugfs_create_blob(name, 0400, dir, &f->wrapper); + if (IS_ERR(file)) { + kfree(f); + return PTR_ERR(file); + } + + f->file =3D file; + list_add(&f->list, list); + + return 0; +} + +int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, + const void *fdt, bool root) +{ + struct dentry *dir; + + if (root) + dir =3D dbg->dir; + else + dir =3D dbg->sub_fdt_dir; + + return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt); +} + +void kho_debugfs_cleanup(struct kho_debugfs *dbg) +{ + struct fdt_debugfs *ff, *tmp; + + list_for_each_entry_safe(ff, tmp, &dbg->fdt_list, list) { + debugfs_remove(ff->file); + list_del(&ff->list); + kfree(ff); + } +} + +static int kho_out_finalize_get(void *data, u64 *val) +{ + *val =3D kho_finalized(); + + return 0; +} + +static int kho_out_finalize_set(void *data, u64 _val) +{ + bool val =3D !!_val; + + if (val) + return kho_finalize(); + + return kho_abort(); +} + +DEFINE_DEBUGFS_ATTRIBUTE(kho_out_finalize_fops, kho_out_finalize_get, + kho_out_finalize_set, "%llu\n"); + +static int scratch_phys_show(struct seq_file *m, void *v) +{ + for (int i =3D 0; i < kho_scratch_cnt; i++) + seq_printf(m, "0x%llx\n", kho_scratch[i].addr); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(scratch_phys); + +static int scratch_len_show(struct seq_file *m, void *v) +{ + for (int i =3D 0; i < kho_scratch_cnt; i++) + seq_printf(m, "0x%llx\n", kho_scratch[i].size); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(scratch_len); + +__init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt) +{ + struct dentry *dir, *sub_fdt_dir; + int err, child; + + INIT_LIST_HEAD(&dbg->fdt_list); + + dir =3D debugfs_create_dir("in", debugfs_root); + if (IS_ERR(dir)) { + err =3D PTR_ERR(dir); + goto err_out; + } + + sub_fdt_dir =3D debugfs_create_dir("sub_fdts", dir); + if (IS_ERR(sub_fdt_dir)) { + err =3D PTR_ERR(sub_fdt_dir); + goto err_rmdir; + } + + err =3D __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt); + if (err) + goto err_rmdir; + + fdt_for_each_subnode(child, fdt, 0) { + int len =3D 0; + const char *name =3D fdt_get_name(fdt, child, NULL); + const u64 *fdt_phys; + + fdt_phys =3D fdt_getprop(fdt, child, "fdt", &len); + if (!fdt_phys) + continue; + if (len !=3D sizeof(*fdt_phys)) { + pr_warn("node %s prop fdt has invalid length: %d\n", + name, len); + continue; + } + err =3D __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name, + phys_to_virt(*fdt_phys)); + if (err) { + pr_warn("failed to add fdt %s to debugfs: %d\n", name, + err); + continue; + } + } + + dbg->dir =3D dir; + dbg->sub_fdt_dir =3D sub_fdt_dir; + + return; +err_rmdir: + debugfs_remove_recursive(dir); +err_out: + /* + * Failure to create /sys/kernel/debug/kho/in does not prevent + * reviving state from KHO and setting up KHO for the next + * kexec. + */ + if (err) + pr_err("failed exposing handover FDT in debugfs: %d\n", err); +} + +__init int kho_out_debugfs_init(struct kho_debugfs *dbg) +{ + struct dentry *dir, *f, *sub_fdt_dir; + + INIT_LIST_HEAD(&dbg->fdt_list); + + dir =3D debugfs_create_dir("out", debugfs_root); + if (IS_ERR(dir)) + return -ENOMEM; + + sub_fdt_dir =3D debugfs_create_dir("sub_fdts", dir); + if (IS_ERR(sub_fdt_dir)) + goto err_rmdir; + + f =3D debugfs_create_file("scratch_phys", 0400, dir, NULL, + &scratch_phys_fops); + if (IS_ERR(f)) + goto err_rmdir; + + f =3D debugfs_create_file("scratch_len", 0400, dir, NULL, + &scratch_len_fops); + if (IS_ERR(f)) + goto err_rmdir; + + f =3D debugfs_create_file("finalize", 0600, dir, NULL, + &kho_out_finalize_fops); + if (IS_ERR(f)) + goto err_rmdir; + + dbg->dir =3D dir; + dbg->sub_fdt_dir =3D sub_fdt_dir; + return 0; + +err_rmdir: + debugfs_remove_recursive(dir); + return -ENOENT; +} + +__init int kho_debugfs_init(void) +{ + debugfs_root =3D debugfs_create_dir("kho", NULL); + if (IS_ERR(debugfs_root)) + return -ENOENT; + return 0; +} diff --git a/kernel/kexec_handover_internal.h b/kernel/kexec_handover_inter= nal.h new file mode 100644 index 000000000000..41e9616fcdd0 --- /dev/null +++ b/kernel/kexec_handover_internal.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LINUX_KEXEC_HANDOVER_INTERNAL_H +#define LINUX_KEXEC_HANDOVER_INTERNAL_H + +#include +#include +#include + +#ifdef CONFIG_KEXEC_HANDOVER_DEBUG +#include + +struct kho_debugfs { + struct dentry *dir; + struct dentry *sub_fdt_dir; + struct list_head fdt_list; +}; + +#else +struct kho_debugfs {} +#endif + +extern struct kho_scratch *kho_scratch; +extern unsigned int kho_scratch_cnt; + +bool kho_finalized(void); + +#ifdef CONFIG_KEXEC_HANDOVER_DEBUG +int kho_debugfs_init(void); +void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt); +int kho_out_debugfs_init(struct kho_debugfs *dbg); +int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, + const void *fdt, bool root); +void kho_debugfs_cleanup(struct kho_debugfs *dbg); +#else +static inline int kho_debugfs_init(void) { return 0; } +static inline void kho_in_debugfs_init(struct kho_debugfs *dbg, + const void *fdt) { } +static inline int kho_out_debugfs_init(struct kho_debugfs *dbg) { return 0= ; } +static inline int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char = *name, + const void *fdt, bool root) { return 0; } +static inline void kho_debugfs_cleanup(struct kho_debugfs *dbg) {} +#endif /* CONFIG_KEXEC_HANDOVER_DEBUG */ + +#endif /* LINUX_KEXEC_HANDOVER_INTERNAL_H */ --=20 2.50.0.727.gbf7dc18ff4-goog