xref: /linux/kernel/liveupdate/luo_flb.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
1cab056f2SPasha Tatashin // SPDX-License-Identifier: GPL-2.0
2cab056f2SPasha Tatashin 
3cab056f2SPasha Tatashin /*
4cab056f2SPasha Tatashin  * Copyright (c) 2025, Google LLC.
5cab056f2SPasha Tatashin  * Pasha Tatashin <pasha.tatashin@soleen.com>
6cab056f2SPasha Tatashin  */
7cab056f2SPasha Tatashin 
8cab056f2SPasha Tatashin /**
9cab056f2SPasha Tatashin  * DOC: LUO File Lifecycle Bound Global Data
10cab056f2SPasha Tatashin  *
11cab056f2SPasha Tatashin  * File-Lifecycle-Bound (FLB) objects provide a mechanism for managing global
12cab056f2SPasha Tatashin  * state that is shared across multiple live-updatable files. The lifecycle of
13cab056f2SPasha Tatashin  * this shared state is tied to the preservation of the files that depend on it.
14cab056f2SPasha Tatashin  *
15cab056f2SPasha Tatashin  * An FLB represents a global resource, such as the IOMMU core state, that is
16cab056f2SPasha Tatashin  * required by multiple file descriptors (e.g., all VFIO fds).
17cab056f2SPasha Tatashin  *
18cab056f2SPasha Tatashin  * The preservation of the FLB's state is triggered when the *first* file
19cab056f2SPasha Tatashin  * depending on it is preserved. The cleanup of this state (unpreserve or
20cab056f2SPasha Tatashin  * finish) is triggered when the *last* file depending on it is unpreserved or
21cab056f2SPasha Tatashin  * finished.
22cab056f2SPasha Tatashin  *
23cab056f2SPasha Tatashin  * Handler Dependency: A file handler declares its dependency on one or more
24cab056f2SPasha Tatashin  * FLBs by registering them via liveupdate_register_flb().
25cab056f2SPasha Tatashin  *
26cab056f2SPasha Tatashin  * Callback Model: Each FLB is defined by a set of operations
27cab056f2SPasha Tatashin  * (&struct liveupdate_flb_ops) that LUO invokes at key points:
28cab056f2SPasha Tatashin  *
29cab056f2SPasha Tatashin  *     - .preserve(): Called for the first file. Saves global state.
30cab056f2SPasha Tatashin  *     - .unpreserve(): Called for the last file (if aborted pre-reboot).
31cab056f2SPasha Tatashin  *     - .retrieve(): Called on-demand in the new kernel to restore the state.
32cab056f2SPasha Tatashin  *     - .finish(): Called for the last file in the new kernel for cleanup.
33cab056f2SPasha Tatashin  *
34cab056f2SPasha Tatashin  * This reference-counted approach ensures that shared state is saved exactly
35cab056f2SPasha Tatashin  * once and restored exactly once, regardless of how many files depend on it,
36cab056f2SPasha Tatashin  * and that its lifecycle is correctly managed across the kexec transition.
37cab056f2SPasha Tatashin  */
38cab056f2SPasha Tatashin 
39cab056f2SPasha Tatashin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
40cab056f2SPasha Tatashin 
41cab056f2SPasha Tatashin #include <linux/cleanup.h>
42cab056f2SPasha Tatashin #include <linux/err.h>
43cab056f2SPasha Tatashin #include <linux/errno.h>
44cab056f2SPasha Tatashin #include <linux/io.h>
45cab056f2SPasha Tatashin #include <linux/kexec_handover.h>
46cab056f2SPasha Tatashin #include <linux/kho/abi/luo.h>
47cab056f2SPasha Tatashin #include <linux/libfdt.h>
48cab056f2SPasha Tatashin #include <linux/list_private.h>
49cab056f2SPasha Tatashin #include <linux/liveupdate.h>
50cab056f2SPasha Tatashin #include <linux/module.h>
51cab056f2SPasha Tatashin #include <linux/mutex.h>
52cab056f2SPasha Tatashin #include <linux/slab.h>
53cab056f2SPasha Tatashin #include <linux/unaligned.h>
54cab056f2SPasha Tatashin #include "luo_internal.h"
55cab056f2SPasha Tatashin 
56cab056f2SPasha Tatashin #define LUO_FLB_PGCNT		1ul
57cab056f2SPasha Tatashin #define LUO_FLB_MAX		(((LUO_FLB_PGCNT << PAGE_SHIFT) -	\
58cab056f2SPasha Tatashin 		sizeof(struct luo_flb_header_ser)) / sizeof(struct luo_flb_ser))
59cab056f2SPasha Tatashin 
60cab056f2SPasha Tatashin struct luo_flb_header {
61cab056f2SPasha Tatashin 	struct luo_flb_header_ser *header_ser;
62cab056f2SPasha Tatashin 	struct luo_flb_ser *ser;
63cab056f2SPasha Tatashin 	bool active;
64cab056f2SPasha Tatashin };
65cab056f2SPasha Tatashin 
66cab056f2SPasha Tatashin struct luo_flb_global {
67cab056f2SPasha Tatashin 	struct luo_flb_header incoming;
68cab056f2SPasha Tatashin 	struct luo_flb_header outgoing;
69cab056f2SPasha Tatashin 	struct list_head list;
70cab056f2SPasha Tatashin 	long count;
71cab056f2SPasha Tatashin };
72cab056f2SPasha Tatashin 
73cab056f2SPasha Tatashin static struct luo_flb_global luo_flb_global = {
74cab056f2SPasha Tatashin 	.list = LIST_HEAD_INIT(luo_flb_global.list),
75cab056f2SPasha Tatashin };
76cab056f2SPasha Tatashin 
77cab056f2SPasha Tatashin /*
78cab056f2SPasha Tatashin  * struct luo_flb_link - Links an FLB definition to a file handler's internal
79cab056f2SPasha Tatashin  * list of dependencies.
80cab056f2SPasha Tatashin  * @flb:  A pointer to the registered &struct liveupdate_flb definition.
81cab056f2SPasha Tatashin  * @list: The list_head for linking.
82cab056f2SPasha Tatashin  */
83cab056f2SPasha Tatashin struct luo_flb_link {
84cab056f2SPasha Tatashin 	struct liveupdate_flb *flb;
85cab056f2SPasha Tatashin 	struct list_head list;
86cab056f2SPasha Tatashin };
87cab056f2SPasha Tatashin 
88cab056f2SPasha Tatashin /* luo_flb_get_private - Access private field, and if needed initialize it. */
luo_flb_get_private(struct liveupdate_flb * flb)89cab056f2SPasha Tatashin static struct luo_flb_private *luo_flb_get_private(struct liveupdate_flb *flb)
90cab056f2SPasha Tatashin {
91cab056f2SPasha Tatashin 	struct luo_flb_private *private = &ACCESS_PRIVATE(flb, private);
92cab056f2SPasha Tatashin 
93cab056f2SPasha Tatashin 	if (!private->initialized) {
94cab056f2SPasha Tatashin 		mutex_init(&private->incoming.lock);
95cab056f2SPasha Tatashin 		mutex_init(&private->outgoing.lock);
96cab056f2SPasha Tatashin 		INIT_LIST_HEAD(&private->list);
97cab056f2SPasha Tatashin 		private->users = 0;
98cab056f2SPasha Tatashin 		private->initialized = true;
99cab056f2SPasha Tatashin 	}
100cab056f2SPasha Tatashin 
101cab056f2SPasha Tatashin 	return private;
102cab056f2SPasha Tatashin }
103cab056f2SPasha Tatashin 
luo_flb_file_preserve_one(struct liveupdate_flb * flb)104cab056f2SPasha Tatashin static int luo_flb_file_preserve_one(struct liveupdate_flb *flb)
105cab056f2SPasha Tatashin {
106cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
107cab056f2SPasha Tatashin 
108cab056f2SPasha Tatashin 	scoped_guard(mutex, &private->outgoing.lock) {
109cab056f2SPasha Tatashin 		if (!private->outgoing.count) {
110cab056f2SPasha Tatashin 			struct liveupdate_flb_op_args args = {0};
111cab056f2SPasha Tatashin 			int err;
112cab056f2SPasha Tatashin 
113cab056f2SPasha Tatashin 			args.flb = flb;
114cab056f2SPasha Tatashin 			err = flb->ops->preserve(&args);
115cab056f2SPasha Tatashin 			if (err)
116cab056f2SPasha Tatashin 				return err;
117cab056f2SPasha Tatashin 			private->outgoing.data = args.data;
118cab056f2SPasha Tatashin 			private->outgoing.obj = args.obj;
119cab056f2SPasha Tatashin 		}
120cab056f2SPasha Tatashin 		private->outgoing.count++;
121cab056f2SPasha Tatashin 	}
122cab056f2SPasha Tatashin 
123cab056f2SPasha Tatashin 	return 0;
124cab056f2SPasha Tatashin }
125cab056f2SPasha Tatashin 
luo_flb_file_unpreserve_one(struct liveupdate_flb * flb)126cab056f2SPasha Tatashin static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb)
127cab056f2SPasha Tatashin {
128cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
129cab056f2SPasha Tatashin 
130cab056f2SPasha Tatashin 	scoped_guard(mutex, &private->outgoing.lock) {
131cab056f2SPasha Tatashin 		private->outgoing.count--;
132cab056f2SPasha Tatashin 		if (!private->outgoing.count) {
133cab056f2SPasha Tatashin 			struct liveupdate_flb_op_args args = {0};
134cab056f2SPasha Tatashin 
135cab056f2SPasha Tatashin 			args.flb = flb;
136cab056f2SPasha Tatashin 			args.data = private->outgoing.data;
137cab056f2SPasha Tatashin 			args.obj = private->outgoing.obj;
138cab056f2SPasha Tatashin 
139cab056f2SPasha Tatashin 			if (flb->ops->unpreserve)
140cab056f2SPasha Tatashin 				flb->ops->unpreserve(&args);
141cab056f2SPasha Tatashin 
142cab056f2SPasha Tatashin 			private->outgoing.data = 0;
143cab056f2SPasha Tatashin 			private->outgoing.obj = NULL;
144cab056f2SPasha Tatashin 		}
145cab056f2SPasha Tatashin 	}
146cab056f2SPasha Tatashin }
147cab056f2SPasha Tatashin 
luo_flb_retrieve_one(struct liveupdate_flb * flb)148cab056f2SPasha Tatashin static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
149cab056f2SPasha Tatashin {
150cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
151cab056f2SPasha Tatashin 	struct luo_flb_header *fh = &luo_flb_global.incoming;
152cab056f2SPasha Tatashin 	struct liveupdate_flb_op_args args = {0};
153cab056f2SPasha Tatashin 	bool found = false;
154cab056f2SPasha Tatashin 	int err;
155cab056f2SPasha Tatashin 
156cab056f2SPasha Tatashin 	guard(mutex)(&private->incoming.lock);
157cab056f2SPasha Tatashin 
158cab056f2SPasha Tatashin 	if (private->incoming.finished)
159cab056f2SPasha Tatashin 		return -ENODATA;
160cab056f2SPasha Tatashin 
161cab056f2SPasha Tatashin 	if (private->incoming.retrieved)
162cab056f2SPasha Tatashin 		return 0;
163cab056f2SPasha Tatashin 
164cab056f2SPasha Tatashin 	if (!fh->active)
165cab056f2SPasha Tatashin 		return -ENODATA;
166cab056f2SPasha Tatashin 
167cab056f2SPasha Tatashin 	for (int i = 0; i < fh->header_ser->count; i++) {
168cab056f2SPasha Tatashin 		if (!strcmp(fh->ser[i].name, flb->compatible)) {
169cab056f2SPasha Tatashin 			private->incoming.data = fh->ser[i].data;
170cab056f2SPasha Tatashin 			private->incoming.count = fh->ser[i].count;
171cab056f2SPasha Tatashin 			found = true;
172cab056f2SPasha Tatashin 			break;
173cab056f2SPasha Tatashin 		}
174cab056f2SPasha Tatashin 	}
175cab056f2SPasha Tatashin 
176cab056f2SPasha Tatashin 	if (!found)
177cab056f2SPasha Tatashin 		return -ENOENT;
178cab056f2SPasha Tatashin 
179cab056f2SPasha Tatashin 	args.flb = flb;
180cab056f2SPasha Tatashin 	args.data = private->incoming.data;
181cab056f2SPasha Tatashin 
182cab056f2SPasha Tatashin 	err = flb->ops->retrieve(&args);
183cab056f2SPasha Tatashin 	if (err)
184cab056f2SPasha Tatashin 		return err;
185cab056f2SPasha Tatashin 
186cab056f2SPasha Tatashin 	private->incoming.obj = args.obj;
187cab056f2SPasha Tatashin 	private->incoming.retrieved = true;
188cab056f2SPasha Tatashin 
189cab056f2SPasha Tatashin 	return 0;
190cab056f2SPasha Tatashin }
191cab056f2SPasha Tatashin 
luo_flb_file_finish_one(struct liveupdate_flb * flb)192cab056f2SPasha Tatashin static void luo_flb_file_finish_one(struct liveupdate_flb *flb)
193cab056f2SPasha Tatashin {
194cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
195cab056f2SPasha Tatashin 	u64 count;
196cab056f2SPasha Tatashin 
197cab056f2SPasha Tatashin 	scoped_guard(mutex, &private->incoming.lock)
198cab056f2SPasha Tatashin 		count = --private->incoming.count;
199cab056f2SPasha Tatashin 
200cab056f2SPasha Tatashin 	if (!count) {
201cab056f2SPasha Tatashin 		struct liveupdate_flb_op_args args = {0};
202cab056f2SPasha Tatashin 
203cab056f2SPasha Tatashin 		if (!private->incoming.retrieved) {
204cab056f2SPasha Tatashin 			int err = luo_flb_retrieve_one(flb);
205cab056f2SPasha Tatashin 
206cab056f2SPasha Tatashin 			if (WARN_ON(err))
207cab056f2SPasha Tatashin 				return;
208cab056f2SPasha Tatashin 		}
209cab056f2SPasha Tatashin 
210cab056f2SPasha Tatashin 		scoped_guard(mutex, &private->incoming.lock) {
211cab056f2SPasha Tatashin 			args.flb = flb;
212cab056f2SPasha Tatashin 			args.obj = private->incoming.obj;
213cab056f2SPasha Tatashin 			flb->ops->finish(&args);
214cab056f2SPasha Tatashin 
215cab056f2SPasha Tatashin 			private->incoming.data = 0;
216cab056f2SPasha Tatashin 			private->incoming.obj = NULL;
217cab056f2SPasha Tatashin 			private->incoming.finished = true;
218cab056f2SPasha Tatashin 		}
219cab056f2SPasha Tatashin 	}
220cab056f2SPasha Tatashin }
221cab056f2SPasha Tatashin 
222cab056f2SPasha Tatashin /**
223cab056f2SPasha Tatashin  * luo_flb_file_preserve - Notifies FLBs that a file is about to be preserved.
224cab056f2SPasha Tatashin  * @fh: The file handler for the preserved file.
225cab056f2SPasha Tatashin  *
226cab056f2SPasha Tatashin  * This function iterates through all FLBs associated with the given file
227cab056f2SPasha Tatashin  * handler. It increments the reference count for each FLB. If the count becomes
228cab056f2SPasha Tatashin  * 1, it triggers the FLB's .preserve() callback to save the global state.
229cab056f2SPasha Tatashin  *
230cab056f2SPasha Tatashin  * This operation is atomic. If any FLB's .preserve() op fails, it will roll
231cab056f2SPasha Tatashin  * back by calling .unpreserve() on any FLBs that were successfully preserved
232cab056f2SPasha Tatashin  * during this call.
233cab056f2SPasha Tatashin  *
234cab056f2SPasha Tatashin  * Context: Called from luo_preserve_file()
235cab056f2SPasha Tatashin  * Return: 0 on success, or a negative errno on failure.
236cab056f2SPasha Tatashin  */
luo_flb_file_preserve(struct liveupdate_file_handler * fh)237cab056f2SPasha Tatashin int luo_flb_file_preserve(struct liveupdate_file_handler *fh)
238cab056f2SPasha Tatashin {
239cab056f2SPasha Tatashin 	struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
240cab056f2SPasha Tatashin 	struct luo_flb_link *iter;
241cab056f2SPasha Tatashin 	int err = 0;
242cab056f2SPasha Tatashin 
243cab056f2SPasha Tatashin 	list_for_each_entry(iter, flb_list, list) {
244cab056f2SPasha Tatashin 		err = luo_flb_file_preserve_one(iter->flb);
245cab056f2SPasha Tatashin 		if (err)
246cab056f2SPasha Tatashin 			goto exit_err;
247cab056f2SPasha Tatashin 	}
248cab056f2SPasha Tatashin 
249cab056f2SPasha Tatashin 	return 0;
250cab056f2SPasha Tatashin 
251cab056f2SPasha Tatashin exit_err:
252cab056f2SPasha Tatashin 	list_for_each_entry_continue_reverse(iter, flb_list, list)
253cab056f2SPasha Tatashin 		luo_flb_file_unpreserve_one(iter->flb);
254cab056f2SPasha Tatashin 
255cab056f2SPasha Tatashin 	return err;
256cab056f2SPasha Tatashin }
257cab056f2SPasha Tatashin 
258cab056f2SPasha Tatashin /**
259cab056f2SPasha Tatashin  * luo_flb_file_unpreserve - Notifies FLBs that a dependent file was unpreserved.
260cab056f2SPasha Tatashin  * @fh: The file handler for the unpreserved file.
261cab056f2SPasha Tatashin  *
262cab056f2SPasha Tatashin  * This function iterates through all FLBs associated with the given file
263cab056f2SPasha Tatashin  * handler, in reverse order of registration. It decrements the reference count
264cab056f2SPasha Tatashin  * for each FLB. If the count becomes 0, it triggers the FLB's .unpreserve()
265cab056f2SPasha Tatashin  * callback to clean up the global state.
266cab056f2SPasha Tatashin  *
267cab056f2SPasha Tatashin  * Context: Called when a preserved file is being cleaned up before reboot
268cab056f2SPasha Tatashin  *          (e.g., from luo_file_unpreserve_files()).
269cab056f2SPasha Tatashin  */
luo_flb_file_unpreserve(struct liveupdate_file_handler * fh)270cab056f2SPasha Tatashin void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh)
271cab056f2SPasha Tatashin {
272cab056f2SPasha Tatashin 	struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
273cab056f2SPasha Tatashin 	struct luo_flb_link *iter;
274cab056f2SPasha Tatashin 
275cab056f2SPasha Tatashin 	list_for_each_entry_reverse(iter, flb_list, list)
276cab056f2SPasha Tatashin 		luo_flb_file_unpreserve_one(iter->flb);
277cab056f2SPasha Tatashin }
278cab056f2SPasha Tatashin 
279cab056f2SPasha Tatashin /**
280cab056f2SPasha Tatashin  * luo_flb_file_finish - Notifies FLBs that a dependent file has been finished.
281cab056f2SPasha Tatashin  * @fh: The file handler for the finished file.
282cab056f2SPasha Tatashin  *
283cab056f2SPasha Tatashin  * This function iterates through all FLBs associated with the given file
284cab056f2SPasha Tatashin  * handler, in reverse order of registration. It decrements the incoming
285cab056f2SPasha Tatashin  * reference count for each FLB. If the count becomes 0, it triggers the FLB's
286cab056f2SPasha Tatashin  * .finish() callback for final cleanup in the new kernel.
287cab056f2SPasha Tatashin  *
288cab056f2SPasha Tatashin  * Context: Called from luo_file_finish() for each file being finished.
289cab056f2SPasha Tatashin  */
luo_flb_file_finish(struct liveupdate_file_handler * fh)290cab056f2SPasha Tatashin void luo_flb_file_finish(struct liveupdate_file_handler *fh)
291cab056f2SPasha Tatashin {
292cab056f2SPasha Tatashin 	struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
293cab056f2SPasha Tatashin 	struct luo_flb_link *iter;
294cab056f2SPasha Tatashin 
295cab056f2SPasha Tatashin 	list_for_each_entry_reverse(iter, flb_list, list)
296cab056f2SPasha Tatashin 		luo_flb_file_finish_one(iter->flb);
297cab056f2SPasha Tatashin }
298cab056f2SPasha Tatashin 
299cab056f2SPasha Tatashin /**
300cab056f2SPasha Tatashin  * liveupdate_register_flb - Associate an FLB with a file handler and register it globally.
301cab056f2SPasha Tatashin  * @fh:   The file handler that will now depend on the FLB.
302cab056f2SPasha Tatashin  * @flb:  The File-Lifecycle-Bound object to associate.
303cab056f2SPasha Tatashin  *
304cab056f2SPasha Tatashin  * Establishes a dependency, informing the LUO core that whenever a file of
305cab056f2SPasha Tatashin  * type @fh is preserved, the state of @flb must also be managed.
306cab056f2SPasha Tatashin  *
307cab056f2SPasha Tatashin  * On the first registration of a given @flb object, it is added to a global
308cab056f2SPasha Tatashin  * registry. This function checks for duplicate registrations, both for a
309cab056f2SPasha Tatashin  * specific handler and globally, and ensures the total number of unique
310cab056f2SPasha Tatashin  * FLBs does not exceed the system limit.
311cab056f2SPasha Tatashin  *
312cab056f2SPasha Tatashin  * Context: Typically called from a subsystem's module init function after
313cab056f2SPasha Tatashin  *          both the handler and the FLB have been defined and initialized.
314cab056f2SPasha Tatashin  * Return: 0 on success. Returns a negative errno on failure:
315cab056f2SPasha Tatashin  *         -EINVAL if arguments are NULL or not initialized.
316cab056f2SPasha Tatashin  *         -ENOMEM on memory allocation failure.
317cab056f2SPasha Tatashin  *         -EEXIST if this FLB is already registered with this handler.
318cab056f2SPasha Tatashin  *         -ENOSPC if the maximum number of global FLBs has been reached.
319cab056f2SPasha Tatashin  *         -EOPNOTSUPP if live update is disabled or not configured.
320cab056f2SPasha Tatashin  */
liveupdate_register_flb(struct liveupdate_file_handler * fh,struct liveupdate_flb * flb)321cab056f2SPasha Tatashin int liveupdate_register_flb(struct liveupdate_file_handler *fh,
322cab056f2SPasha Tatashin 			    struct liveupdate_flb *flb)
323cab056f2SPasha Tatashin {
324cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
325cab056f2SPasha Tatashin 	struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
326cab056f2SPasha Tatashin 	struct luo_flb_link *link __free(kfree) = NULL;
327cab056f2SPasha Tatashin 	struct liveupdate_flb *gflb;
328cab056f2SPasha Tatashin 	struct luo_flb_link *iter;
329cab056f2SPasha Tatashin 	int err;
330cab056f2SPasha Tatashin 
331cab056f2SPasha Tatashin 	if (!liveupdate_enabled())
332cab056f2SPasha Tatashin 		return -EOPNOTSUPP;
333cab056f2SPasha Tatashin 
334cab056f2SPasha Tatashin 	if (WARN_ON(!flb->ops->preserve || !flb->ops->unpreserve ||
335cab056f2SPasha Tatashin 		    !flb->ops->retrieve || !flb->ops->finish)) {
336cab056f2SPasha Tatashin 		return -EINVAL;
337cab056f2SPasha Tatashin 	}
338cab056f2SPasha Tatashin 
339cab056f2SPasha Tatashin 	/*
340cab056f2SPasha Tatashin 	 * File handler must already be registered, as it initializes the
341cab056f2SPasha Tatashin 	 * flb_list
342cab056f2SPasha Tatashin 	 */
343cab056f2SPasha Tatashin 	if (WARN_ON(list_empty(&ACCESS_PRIVATE(fh, list))))
344cab056f2SPasha Tatashin 		return -EINVAL;
345cab056f2SPasha Tatashin 
346*bf4afc53SLinus Torvalds 	link = kzalloc_obj(*link);
347cab056f2SPasha Tatashin 	if (!link)
348cab056f2SPasha Tatashin 		return -ENOMEM;
349cab056f2SPasha Tatashin 
350cab056f2SPasha Tatashin 	/*
351cab056f2SPasha Tatashin 	 * Ensure the system is quiescent (no active sessions).
352cab056f2SPasha Tatashin 	 * This acts as a global lock for registration: no other thread can
353cab056f2SPasha Tatashin 	 * be in this section, and no sessions can be creating/using FDs.
354cab056f2SPasha Tatashin 	 */
355cab056f2SPasha Tatashin 	if (!luo_session_quiesce())
356cab056f2SPasha Tatashin 		return -EBUSY;
357cab056f2SPasha Tatashin 
358cab056f2SPasha Tatashin 	/* Check that this FLB is not already linked to this file handler */
359cab056f2SPasha Tatashin 	err = -EEXIST;
360cab056f2SPasha Tatashin 	list_for_each_entry(iter, flb_list, list) {
361cab056f2SPasha Tatashin 		if (iter->flb == flb)
362cab056f2SPasha Tatashin 			goto err_resume;
363cab056f2SPasha Tatashin 	}
364cab056f2SPasha Tatashin 
365cab056f2SPasha Tatashin 	/*
366cab056f2SPasha Tatashin 	 * If this FLB is not linked to global list it's the first time the FLB
367cab056f2SPasha Tatashin 	 * is registered
368cab056f2SPasha Tatashin 	 */
369cab056f2SPasha Tatashin 	if (!private->users) {
370cab056f2SPasha Tatashin 		if (WARN_ON(!list_empty(&private->list))) {
371cab056f2SPasha Tatashin 			err = -EINVAL;
372cab056f2SPasha Tatashin 			goto err_resume;
373cab056f2SPasha Tatashin 		}
374cab056f2SPasha Tatashin 
375cab056f2SPasha Tatashin 		if (luo_flb_global.count == LUO_FLB_MAX) {
376cab056f2SPasha Tatashin 			err = -ENOSPC;
377cab056f2SPasha Tatashin 			goto err_resume;
378cab056f2SPasha Tatashin 		}
379cab056f2SPasha Tatashin 
380cab056f2SPasha Tatashin 		/* Check that compatible string is unique in global list */
381cab056f2SPasha Tatashin 		list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
382cab056f2SPasha Tatashin 			if (!strcmp(gflb->compatible, flb->compatible))
383cab056f2SPasha Tatashin 				goto err_resume;
384cab056f2SPasha Tatashin 		}
385cab056f2SPasha Tatashin 
386cab056f2SPasha Tatashin 		if (!try_module_get(flb->ops->owner)) {
387cab056f2SPasha Tatashin 			err = -EAGAIN;
388cab056f2SPasha Tatashin 			goto err_resume;
389cab056f2SPasha Tatashin 		}
390cab056f2SPasha Tatashin 
391cab056f2SPasha Tatashin 		list_add_tail(&private->list, &luo_flb_global.list);
392cab056f2SPasha Tatashin 		luo_flb_global.count++;
393cab056f2SPasha Tatashin 	}
394cab056f2SPasha Tatashin 
395cab056f2SPasha Tatashin 	/* Finally, link the FLB to the file handler */
396cab056f2SPasha Tatashin 	private->users++;
397cab056f2SPasha Tatashin 	link->flb = flb;
398cab056f2SPasha Tatashin 	list_add_tail(&no_free_ptr(link)->list, flb_list);
399cab056f2SPasha Tatashin 	luo_session_resume();
400cab056f2SPasha Tatashin 
401cab056f2SPasha Tatashin 	return 0;
402cab056f2SPasha Tatashin 
403cab056f2SPasha Tatashin err_resume:
404cab056f2SPasha Tatashin 	luo_session_resume();
405cab056f2SPasha Tatashin 	return err;
406cab056f2SPasha Tatashin }
407cab056f2SPasha Tatashin 
408cab056f2SPasha Tatashin /**
409cab056f2SPasha Tatashin  * liveupdate_unregister_flb - Remove an FLB dependency from a file handler.
410cab056f2SPasha Tatashin  * @fh:   The file handler that is currently depending on the FLB.
411cab056f2SPasha Tatashin  * @flb:  The File-Lifecycle-Bound object to remove.
412cab056f2SPasha Tatashin  *
413cab056f2SPasha Tatashin  * Removes the association between the specified file handler and the FLB
414cab056f2SPasha Tatashin  * previously established by liveupdate_register_flb().
415cab056f2SPasha Tatashin  *
416cab056f2SPasha Tatashin  * This function manages the global lifecycle of the FLB. It decrements the
417cab056f2SPasha Tatashin  * FLB's usage count. If this was the last file handler referencing this FLB,
418cab056f2SPasha Tatashin  * the FLB is removed from the global registry and the reference to its
419cab056f2SPasha Tatashin  * owner module (acquired during registration) is released.
420cab056f2SPasha Tatashin  *
421cab056f2SPasha Tatashin  * Context: This function ensures the session is quiesced (no active FDs
422cab056f2SPasha Tatashin  *          being created) during the update. It is typically called from a
423cab056f2SPasha Tatashin  *          subsystem's module exit function.
424cab056f2SPasha Tatashin  * Return: 0 on success.
425cab056f2SPasha Tatashin  *         -EOPNOTSUPP if live update is disabled.
426cab056f2SPasha Tatashin  *         -EBUSY if the live update session is active and cannot be quiesced.
427cab056f2SPasha Tatashin  *         -ENOENT if the FLB was not found in the file handler's list.
428cab056f2SPasha Tatashin  */
liveupdate_unregister_flb(struct liveupdate_file_handler * fh,struct liveupdate_flb * flb)429cab056f2SPasha Tatashin int liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
430cab056f2SPasha Tatashin 			      struct liveupdate_flb *flb)
431cab056f2SPasha Tatashin {
432cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
433cab056f2SPasha Tatashin 	struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
434cab056f2SPasha Tatashin 	struct luo_flb_link *iter;
435cab056f2SPasha Tatashin 	int err = -ENOENT;
436cab056f2SPasha Tatashin 
437cab056f2SPasha Tatashin 	if (!liveupdate_enabled())
438cab056f2SPasha Tatashin 		return -EOPNOTSUPP;
439cab056f2SPasha Tatashin 
440cab056f2SPasha Tatashin 	/*
441cab056f2SPasha Tatashin 	 * Ensure the system is quiescent (no active sessions).
442cab056f2SPasha Tatashin 	 * This acts as a global lock for unregistration.
443cab056f2SPasha Tatashin 	 */
444cab056f2SPasha Tatashin 	if (!luo_session_quiesce())
445cab056f2SPasha Tatashin 		return -EBUSY;
446cab056f2SPasha Tatashin 
447cab056f2SPasha Tatashin 	/* Find and remove the link from the file handler's list */
448cab056f2SPasha Tatashin 	list_for_each_entry(iter, flb_list, list) {
449cab056f2SPasha Tatashin 		if (iter->flb == flb) {
450cab056f2SPasha Tatashin 			list_del(&iter->list);
451cab056f2SPasha Tatashin 			kfree(iter);
452cab056f2SPasha Tatashin 			err = 0;
453cab056f2SPasha Tatashin 			break;
454cab056f2SPasha Tatashin 		}
455cab056f2SPasha Tatashin 	}
456cab056f2SPasha Tatashin 
457cab056f2SPasha Tatashin 	if (err)
458cab056f2SPasha Tatashin 		goto err_resume;
459cab056f2SPasha Tatashin 
460cab056f2SPasha Tatashin 	private->users--;
461cab056f2SPasha Tatashin 	/*
462cab056f2SPasha Tatashin 	 * If this is the last file-handler with which we are registred, remove
463cab056f2SPasha Tatashin 	 * from the global list, and relese module reference.
464cab056f2SPasha Tatashin 	 */
465cab056f2SPasha Tatashin 	if (!private->users) {
466cab056f2SPasha Tatashin 		list_del_init(&private->list);
467cab056f2SPasha Tatashin 		luo_flb_global.count--;
468cab056f2SPasha Tatashin 		module_put(flb->ops->owner);
469cab056f2SPasha Tatashin 	}
470cab056f2SPasha Tatashin 
471cab056f2SPasha Tatashin 	luo_session_resume();
472cab056f2SPasha Tatashin 
473cab056f2SPasha Tatashin 	return 0;
474cab056f2SPasha Tatashin 
475cab056f2SPasha Tatashin err_resume:
476cab056f2SPasha Tatashin 	luo_session_resume();
477cab056f2SPasha Tatashin 	return err;
478cab056f2SPasha Tatashin }
479cab056f2SPasha Tatashin 
480cab056f2SPasha Tatashin /**
481cab056f2SPasha Tatashin  * liveupdate_flb_get_incoming - Retrieve the incoming FLB object.
482cab056f2SPasha Tatashin  * @flb:  The FLB definition.
483cab056f2SPasha Tatashin  * @objp: Output parameter; will be populated with the live shared object.
484cab056f2SPasha Tatashin  *
485cab056f2SPasha Tatashin  * Returns a pointer to its shared live object for the incoming (post-reboot)
486cab056f2SPasha Tatashin  * path.
487cab056f2SPasha Tatashin  *
488cab056f2SPasha Tatashin  * If this is the first time the object is requested in the new kernel, this
489cab056f2SPasha Tatashin  * function will trigger the FLB's .retrieve() callback to reconstruct the
490cab056f2SPasha Tatashin  * object from its preserved state. Subsequent calls will return the same
491cab056f2SPasha Tatashin  * cached object.
492cab056f2SPasha Tatashin  *
493cab056f2SPasha Tatashin  * Return: 0 on success, or a negative errno on failure. -ENODATA means no
494cab056f2SPasha Tatashin  * incoming FLB data, -ENOENT means specific flb not found in the incoming
495cab056f2SPasha Tatashin  * data, and -EOPNOTSUPP when live update is disabled or not configured.
496cab056f2SPasha Tatashin  */
liveupdate_flb_get_incoming(struct liveupdate_flb * flb,void ** objp)497cab056f2SPasha Tatashin int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp)
498cab056f2SPasha Tatashin {
499cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
500cab056f2SPasha Tatashin 
501cab056f2SPasha Tatashin 	if (!liveupdate_enabled())
502cab056f2SPasha Tatashin 		return -EOPNOTSUPP;
503cab056f2SPasha Tatashin 
504cab056f2SPasha Tatashin 	if (!private->incoming.obj) {
505cab056f2SPasha Tatashin 		int err = luo_flb_retrieve_one(flb);
506cab056f2SPasha Tatashin 
507cab056f2SPasha Tatashin 		if (err)
508cab056f2SPasha Tatashin 			return err;
509cab056f2SPasha Tatashin 	}
510cab056f2SPasha Tatashin 
511cab056f2SPasha Tatashin 	guard(mutex)(&private->incoming.lock);
512cab056f2SPasha Tatashin 	*objp = private->incoming.obj;
513cab056f2SPasha Tatashin 
514cab056f2SPasha Tatashin 	return 0;
515cab056f2SPasha Tatashin }
516cab056f2SPasha Tatashin 
517cab056f2SPasha Tatashin /**
518cab056f2SPasha Tatashin  * liveupdate_flb_get_outgoing - Retrieve the outgoing FLB object.
519cab056f2SPasha Tatashin  * @flb:  The FLB definition.
520cab056f2SPasha Tatashin  * @objp: Output parameter; will be populated with the live shared object.
521cab056f2SPasha Tatashin  *
522cab056f2SPasha Tatashin  * Returns a pointer to its shared live object for the outgoing (pre-reboot)
523cab056f2SPasha Tatashin  * path.
524cab056f2SPasha Tatashin  *
525cab056f2SPasha Tatashin  * This function assumes the object has already been created by the FLB's
526cab056f2SPasha Tatashin  * .preserve() callback, which is triggered when the first dependent file
527cab056f2SPasha Tatashin  * is preserved.
528cab056f2SPasha Tatashin  *
529cab056f2SPasha Tatashin  * Return: 0 on success, or a negative errno on failure.
530cab056f2SPasha Tatashin  */
liveupdate_flb_get_outgoing(struct liveupdate_flb * flb,void ** objp)531cab056f2SPasha Tatashin int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp)
532cab056f2SPasha Tatashin {
533cab056f2SPasha Tatashin 	struct luo_flb_private *private = luo_flb_get_private(flb);
534cab056f2SPasha Tatashin 
535cab056f2SPasha Tatashin 	if (!liveupdate_enabled())
536cab056f2SPasha Tatashin 		return -EOPNOTSUPP;
537cab056f2SPasha Tatashin 
538cab056f2SPasha Tatashin 	guard(mutex)(&private->outgoing.lock);
539cab056f2SPasha Tatashin 	*objp = private->outgoing.obj;
540cab056f2SPasha Tatashin 
541cab056f2SPasha Tatashin 	return 0;
542cab056f2SPasha Tatashin }
543cab056f2SPasha Tatashin 
luo_flb_setup_outgoing(void * fdt_out)544cab056f2SPasha Tatashin int __init luo_flb_setup_outgoing(void *fdt_out)
545cab056f2SPasha Tatashin {
546cab056f2SPasha Tatashin 	struct luo_flb_header_ser *header_ser;
547cab056f2SPasha Tatashin 	u64 header_ser_pa;
548cab056f2SPasha Tatashin 	int err;
549cab056f2SPasha Tatashin 
550cab056f2SPasha Tatashin 	header_ser = kho_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT);
551cab056f2SPasha Tatashin 	if (IS_ERR(header_ser))
552cab056f2SPasha Tatashin 		return PTR_ERR(header_ser);
553cab056f2SPasha Tatashin 
554cab056f2SPasha Tatashin 	header_ser_pa = virt_to_phys(header_ser);
555cab056f2SPasha Tatashin 
556cab056f2SPasha Tatashin 	err = fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME);
557cab056f2SPasha Tatashin 	err |= fdt_property_string(fdt_out, "compatible",
558cab056f2SPasha Tatashin 				   LUO_FDT_FLB_COMPATIBLE);
559cab056f2SPasha Tatashin 	err |= fdt_property(fdt_out, LUO_FDT_FLB_HEADER, &header_ser_pa,
560cab056f2SPasha Tatashin 			    sizeof(header_ser_pa));
561cab056f2SPasha Tatashin 	err |= fdt_end_node(fdt_out);
562cab056f2SPasha Tatashin 
563cab056f2SPasha Tatashin 	if (err)
564cab056f2SPasha Tatashin 		goto err_unpreserve;
565cab056f2SPasha Tatashin 
566cab056f2SPasha Tatashin 	header_ser->pgcnt = LUO_FLB_PGCNT;
567cab056f2SPasha Tatashin 	luo_flb_global.outgoing.header_ser = header_ser;
568cab056f2SPasha Tatashin 	luo_flb_global.outgoing.ser = (void *)(header_ser + 1);
569cab056f2SPasha Tatashin 	luo_flb_global.outgoing.active = true;
570cab056f2SPasha Tatashin 
571cab056f2SPasha Tatashin 	return 0;
572cab056f2SPasha Tatashin 
573cab056f2SPasha Tatashin err_unpreserve:
574cab056f2SPasha Tatashin 	kho_unpreserve_free(header_ser);
575cab056f2SPasha Tatashin 
576cab056f2SPasha Tatashin 	return err;
577cab056f2SPasha Tatashin }
578cab056f2SPasha Tatashin 
luo_flb_setup_incoming(void * fdt_in)579cab056f2SPasha Tatashin int __init luo_flb_setup_incoming(void *fdt_in)
580cab056f2SPasha Tatashin {
581cab056f2SPasha Tatashin 	struct luo_flb_header_ser *header_ser;
582cab056f2SPasha Tatashin 	int err, header_size, offset;
583cab056f2SPasha Tatashin 	const void *ptr;
584cab056f2SPasha Tatashin 	u64 header_ser_pa;
585cab056f2SPasha Tatashin 
586cab056f2SPasha Tatashin 	offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME);
587cab056f2SPasha Tatashin 	if (offset < 0) {
588cab056f2SPasha Tatashin 		pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME);
589cab056f2SPasha Tatashin 
590cab056f2SPasha Tatashin 		return -ENOENT;
591cab056f2SPasha Tatashin 	}
592cab056f2SPasha Tatashin 
593cab056f2SPasha Tatashin 	err = fdt_node_check_compatible(fdt_in, offset,
594cab056f2SPasha Tatashin 					LUO_FDT_FLB_COMPATIBLE);
595cab056f2SPasha Tatashin 	if (err) {
596cab056f2SPasha Tatashin 		pr_err("FLB node is incompatible with '%s' [%d]\n",
597cab056f2SPasha Tatashin 		       LUO_FDT_FLB_COMPATIBLE, err);
598cab056f2SPasha Tatashin 
599cab056f2SPasha Tatashin 		return -EINVAL;
600cab056f2SPasha Tatashin 	}
601cab056f2SPasha Tatashin 
602cab056f2SPasha Tatashin 	header_size = 0;
603cab056f2SPasha Tatashin 	ptr = fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEADER, &header_size);
604cab056f2SPasha Tatashin 	if (!ptr || header_size != sizeof(u64)) {
605cab056f2SPasha Tatashin 		pr_err("Unable to get FLB header property '%s' [%d]\n",
606cab056f2SPasha Tatashin 		       LUO_FDT_FLB_HEADER, header_size);
607cab056f2SPasha Tatashin 
608cab056f2SPasha Tatashin 		return -EINVAL;
609cab056f2SPasha Tatashin 	}
610cab056f2SPasha Tatashin 
611cab056f2SPasha Tatashin 	header_ser_pa = get_unaligned((u64 *)ptr);
612cab056f2SPasha Tatashin 	header_ser = phys_to_virt(header_ser_pa);
613cab056f2SPasha Tatashin 
614cab056f2SPasha Tatashin 	luo_flb_global.incoming.header_ser = header_ser;
615cab056f2SPasha Tatashin 	luo_flb_global.incoming.ser = (void *)(header_ser + 1);
616cab056f2SPasha Tatashin 	luo_flb_global.incoming.active = true;
617cab056f2SPasha Tatashin 
618cab056f2SPasha Tatashin 	return 0;
619cab056f2SPasha Tatashin }
620cab056f2SPasha Tatashin 
621cab056f2SPasha Tatashin /**
622cab056f2SPasha Tatashin  * luo_flb_serialize - Serializes all active FLB objects for KHO.
623cab056f2SPasha Tatashin  *
624cab056f2SPasha Tatashin  * This function is called from the reboot path. It iterates through all
625cab056f2SPasha Tatashin  * registered File-Lifecycle-Bound (FLB) objects. For each FLB that has been
626cab056f2SPasha Tatashin  * preserved (i.e., its reference count is greater than zero), it writes its
627cab056f2SPasha Tatashin  * metadata into the memory region designated for Kexec Handover.
628cab056f2SPasha Tatashin  *
629cab056f2SPasha Tatashin  * The serialized data includes the FLB's compatibility string, its opaque
630cab056f2SPasha Tatashin  * data handle, and the final reference count. This allows the new kernel to
631cab056f2SPasha Tatashin  * find the appropriate handler and reconstruct the FLB's state.
632cab056f2SPasha Tatashin  *
633cab056f2SPasha Tatashin  * Context: Called from liveupdate_reboot() just before kho_finalize().
634cab056f2SPasha Tatashin  */
luo_flb_serialize(void)635cab056f2SPasha Tatashin void luo_flb_serialize(void)
636cab056f2SPasha Tatashin {
637cab056f2SPasha Tatashin 	struct luo_flb_header *fh = &luo_flb_global.outgoing;
638cab056f2SPasha Tatashin 	struct liveupdate_flb *gflb;
639cab056f2SPasha Tatashin 	int i = 0;
640cab056f2SPasha Tatashin 
641cab056f2SPasha Tatashin 	list_private_for_each_entry(gflb, &luo_flb_global.list, private.list) {
642cab056f2SPasha Tatashin 		struct luo_flb_private *private = luo_flb_get_private(gflb);
643cab056f2SPasha Tatashin 
644cab056f2SPasha Tatashin 		if (private->outgoing.count > 0) {
645cab056f2SPasha Tatashin 			strscpy(fh->ser[i].name, gflb->compatible,
646cab056f2SPasha Tatashin 				sizeof(fh->ser[i].name));
647cab056f2SPasha Tatashin 			fh->ser[i].data = private->outgoing.data;
648cab056f2SPasha Tatashin 			fh->ser[i].count = private->outgoing.count;
649cab056f2SPasha Tatashin 			i++;
650cab056f2SPasha Tatashin 		}
651cab056f2SPasha Tatashin 	}
652cab056f2SPasha Tatashin 
653cab056f2SPasha Tatashin 	fh->header_ser->count = i;
654cab056f2SPasha Tatashin }
655