summaryrefslogtreecommitdiff
path: root/include/bootflow.h
blob: 6aa3d1fff8d30c6a55af97234a0d209309c1d364 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright 2021 Google LLC
 * Written by Simon Glass <sjg@chromium.org>
 */

#ifndef __bootflow_h
#define __bootflow_h

#include <linux/list.h>

/**
 * enum bootflow_state_t - states that a particular bootflow can be in
 *
 * Only bootflows in state BOOTFLOWST_READY can be used to boot.
 *
 * See bootflow_state[] for the names for each of these
 */
enum bootflow_state_t {
	BOOTFLOWST_BASE,	/**< Nothing known yet */
	BOOTFLOWST_MEDIA,	/**< Media exists */
	BOOTFLOWST_PART,	/**< Partition exists */
	BOOTFLOWST_FS,		/**< Filesystem exists */
	BOOTFLOWST_FILE,	/**< Bootflow file exists */
	BOOTFLOWST_READY,	/**< Bootflow file loaded */

	BOOTFLOWST_COUNT
};

/**
 * struct bootflow - information about a bootflow
 *
 * This is connected into two separate linked lists:
 *
 *   bm_sibling - links all bootflows in the same bootdev
 *   glob_sibling - links all bootflows in all bootdevs
 *
 * @bm_node: Points to siblings in the same bootdev
 * @glob_node: Points to siblings in the global list (all bootdev)
 * @dev: Bootdevice device which produced this bootflow
 * @blk: Block device which contains this bootflow, NULL if this is a network
 *	device
 * @part: Partition number (0 for whole device)
 * @fs_type: Filesystem type (FS_TYPE...) if this is fixed by the media, else 0.
 *	For example, the sandbox host-filesystem bootdev sets this to
 *	FS_TYPE_SANDBOX
 * @method: Bootmethod device used to perform the boot and read files
 * @name: Name of bootflow (allocated)
 * @state: Current state (enum bootflow_state_t)
 * @subdir: Subdirectory to fetch files from (with trailing /), or NULL if none
 * @fname: Filename of bootflow file (allocated)
 * @buf: Bootflow file contents (allocated)
 * @size: Size of bootflow file in bytes
 * @err: Error number received (0 if OK)
 */
struct bootflow {
	struct list_head bm_node;
	struct list_head glob_node;
	struct udevice *dev;
	struct udevice *blk;
	int part;
	int fs_type;
	struct udevice *method;
	char *name;
	enum bootflow_state_t state;
	char *subdir;
	char *fname;
	char *buf;
	int size;
	int err;
};

/**
 * enum bootflow_flags_t - flags for the bootflow iterator
 *
 * @BOOTFLOWF_FIXED: Only used fixed/internal media
 * @BOOTFLOWF_SHOW: Show each bootdev before scanning it
 * @BOOTFLOWF_ALL: Return bootflows with errors as well
 * @BOOTFLOWF_SINGLE_DEV: Just scan one bootmeth
 * @BOOTFLOWF_SKIP_GLOBAL: Don't scan global bootmeths
 */
enum bootflow_flags_t {
	BOOTFLOWF_FIXED		= 1 << 0,
	BOOTFLOWF_SHOW		= 1 << 1,
	BOOTFLOWF_ALL		= 1 << 2,
	BOOTFLOWF_SINGLE_DEV	= 1 << 3,
	BOOTFLOWF_SKIP_GLOBAL	= 1 << 4,
};

/**
 * struct bootflow_iter - state for iterating through bootflows
 *
 * This starts at with the first bootdev/partition/bootmeth and can be used to
 * iterate through all of them.
 *
 * Iteration starts with the bootdev. The first partition (0, i.e. whole device)
 * is scanned first. For partition 0, it iterates through all the available
 * bootmeths to see which one(s) can provide a bootflow. Then it moves to
 * parition 1 (if there is one) and the process continues. Once all partitions
 * are examined, it moves to the next bootdev.
 *
 * Initially @max_part is 0, meaning that only the whole device (@part=0) can be
 * used. During scanning, if a partition table is found, then @max_part is
 * updated to a larger value, no less than the number of available partitions.
 * This ensures that iteration works through all partitions on the bootdev.
 *
 * @flags: Flags to use (see enum bootflow_flags_t). If BOOTFLOWF_GLOBAL_FIRST is
 *	enabled then the global bootmeths are being scanned, otherwise we have
 *	moved onto the bootdevs
 * @dev: Current bootdev, NULL if none
 * @part: Current partition number (0 for whole device)
 * @method: Current bootmeth
 * @max_part: Maximum hardware partition number in @dev, 0 if there is no
 *	partition table
 * @err: Error obtained from checking the last iteration. This is used to skip
 *	forward (e.g. to skip the current partition because it is not valid)
 *	-ESHUTDOWN: try next bootdev
 * @num_devs: Number of bootdevs in @dev_order
 * @cur_dev: Current bootdev number, an index into @dev_order[]
 * @dev_order: List of bootdevs to scan, in order of priority. The scan starts
 *	with the first one on the list
 * @num_methods: Number of bootmeth devices in @method_order
 * @cur_method: Current method number, an index into @method_order
 * @first_glob_method: First global method, if any, else -1
 * @method_order: List of bootmeth devices to use, in order. The normal methods
 *	appear first, then the global ones, if any
 * @doing_global: true if we are iterating through the global bootmeths (which
 *	happens before the normal ones)
 */
struct bootflow_iter {
	int flags;
	struct udevice *dev;
	int part;
	struct udevice *method;
	int max_part;
	int err;
	int num_devs;
	int cur_dev;
	struct udevice **dev_order;
	int num_methods;
	int cur_method;
	int first_glob_method;
	struct udevice **method_order;
	bool doing_global;
};

/**
 * bootflow_iter_init() - Reset a bootflow iterator
 *
 * This sets everything to the starting point, ready for use.
 *
 * @iter: Place to store private info (inited by this call)
 * @flags: Flags to use (see enum bootflow_flags_t)
 */
void bootflow_iter_init(struct bootflow_iter *iter, int flags);

/**
 * bootflow_iter_uninit() - Free memory used by an interator
 *
 * @iter:	Iterator to free
 */
void bootflow_iter_uninit(struct bootflow_iter *iter);

/**
 * bootflow_iter_drop_bootmeth() - Remove a bootmeth from an iterator
 *
 * Update the iterator so that the bootmeth will not be used again while this
 * iterator is in use
 *
 * @iter: Iterator to update
 * @bmeth: Boot method to remove
 */
int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
				const struct udevice *bmeth);

/**
 * bootflow_scan_bootdev() - find the first bootflow in a bootdev
 *
 * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too
 *
 * @dev:	Boot device to scan, NULL to work through all of them until it
 *	finds one that can supply a bootflow
 * @iter:	Place to store private info (inited by this call)
 * @flags:	Flags for iterator (enum bootflow_flags_t)
 * @bflow:	Place to put the bootflow if found
 * Return: 0 if found,  -ENODEV if no device, other -ve on other error
 *	(iteration can continue)
 */
int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
			  int flags, struct bootflow *bflow);

/**
 * bootflow_scan_first() - find the first bootflow
 *
 * This works through the available bootdev devices until it finds one that
 * can supply a bootflow. It then returns that
 *
 * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too
 *
 * @iter:	Place to store private info (inited by this call), with
 * @flags:	Flags for bootdev (enum bootflow_flags_t)
 * @bflow:	Place to put the bootflow if found
 * Return: 0 if found, -ENODEV if no device, other -ve on other error (iteration
 *	can continue)
 */
int bootflow_scan_first(struct bootflow_iter *iter, int flags,
			struct bootflow *bflow);

/**
 * bootflow_scan_next() - find the next bootflow
 *
 * This works through the available bootdev devices until it finds one that
 * can supply a bootflow. It then returns that bootflow
 *
 * @iter:	Private info (as set up by bootflow_scan_first())
 * @bflow:	Place to put the bootflow if found
 * Return: 0 if found, -ENODEV if no device, -ESHUTDOWN if no more bootflows,
 *	other -ve on other error (iteration can continue)
 */
int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow);

/**
 * bootflow_first_glob() - Get the first bootflow from the global list
 *
 * Returns the first bootflow in the global list, no matter what bootflow it is
 * attached to
 *
 * @bflowp: Returns a pointer to the bootflow
 * Return: 0 if found, -ENOENT if there are no bootflows
 */
int bootflow_first_glob(struct bootflow **bflowp);

/**
 * bootflow_next_glob() - Get the next bootflow from the global list
 *
 * Returns the next bootflow in the global list, no matter what bootflow it is
 * attached to
 *
 * @bflowp: On entry, the last bootflow returned , e.g. from
 *	bootflow_first_glob()
 * Return: 0 if found, -ENOENT if there are no more bootflows
 */
int bootflow_next_glob(struct bootflow **bflowp);

/**
 * bootflow_free() - Free memory used by a bootflow
 *
 * This frees fields within @bflow, but not the @bflow pointer itself
 */
void bootflow_free(struct bootflow *bflow);

/**
 * bootflow_boot() - boot a bootflow
 *
 * @bflow: Bootflow to boot
 * Return: -EPROTO if bootflow has not been loaded, -ENOSYS if the bootflow
 *	type is not supported, -EFAULT if the boot returned without an error
 *	when we are expecting it to boot, -ENOTSUPP if trying method resulted in
 *	finding out that is not actually supported for this boot and should not
 *	be tried again unless something changes
 */
int bootflow_boot(struct bootflow *bflow);

/**
 * bootflow_run_boot() - Try to boot a bootflow
 *
 * @iter: Current iteration (or NULL if none). Used to disable a bootmeth if the
 *	boot returns -ENOTSUPP
 * @bflow: Bootflow to boot
 * Return: result of trying to boot
 */
int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow);

/**
 * bootflow_state_get_name() - Get the name of a bootflow state
 *
 * @state: State to check
 * Return: name, or "?" if invalid
 */
const char *bootflow_state_get_name(enum bootflow_state_t state);

/**
 * bootflow_remove() - Remove a bootflow and free its memory
 *
 * This updates the linked lists containing the bootflow then frees it.
 *
 * @bflow: Bootflow to remove
 */
void bootflow_remove(struct bootflow *bflow);

/**
 * bootflow_iter_uses_blk_dev() - Check that a bootflow uses a block device
 *
 * This checks the bootdev in the bootflow to make sure it uses a block device
 *
 * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet)
 */
int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter);

/**
 * bootflow_iter_uses_network() - Check that a bootflow uses a network device
 *
 * This checks the bootdev in the bootflow to make sure it uses a network
 * device
 *
 * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC)
 */
int bootflow_iter_uses_network(const struct bootflow_iter *iter);

/**
 * bootflow_iter_uses_system() - Check that a bootflow uses the bootstd device
 *
 * This checks the bootdev in the bootflow to make sure it uses the bootstd
 * device
 *
 * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC)
 */
int bootflow_iter_uses_system(const struct bootflow_iter *iter);

#endif