/src/u-boot/boot/upl_read.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * UPL handoff parsing |
4 | | * |
5 | | * Copyright 2024 Google LLC |
6 | | * Written by Simon Glass <sjg@chromium.org> |
7 | | */ |
8 | | |
9 | | #define LOG_CATEGORY UCLASS_BOOTSTD |
10 | | |
11 | | #include <log.h> |
12 | | #include <upl.h> |
13 | | #include <dm/ofnode.h> |
14 | | #include "upl_common.h" |
15 | | |
16 | | /** |
17 | | * read_addr() - Read an address |
18 | | * |
19 | | * Reads an address in the correct format, either 32- or 64-bit |
20 | | * |
21 | | * @upl: UPL state |
22 | | * @node: Node to read from |
23 | | * @prop: Property name to read |
24 | | * @addr: Place to put the address |
25 | | * Return: 0 if OK, -ve on error |
26 | | */ |
27 | | static int read_addr(const struct upl *upl, ofnode node, const char *prop, |
28 | | ulong *addrp) |
29 | 0 | { |
30 | 0 | int ret; |
31 | |
|
32 | 0 | if (upl->addr_cells == 1) { |
33 | 0 | u32 val; |
34 | |
|
35 | 0 | ret = ofnode_read_u32(node, prop, &val); |
36 | 0 | if (!ret) |
37 | 0 | *addrp = val; |
38 | 0 | } else { |
39 | 0 | u64 val; |
40 | |
|
41 | 0 | ret = ofnode_read_u64(node, prop, &val); |
42 | 0 | if (!ret) |
43 | 0 | *addrp = val; |
44 | 0 | } |
45 | |
|
46 | 0 | return ret; |
47 | 0 | } |
48 | | |
49 | | /** |
50 | | * read_size() - Read a size |
51 | | * |
52 | | * Reads a size in the correct format, either 32- or 64-bit |
53 | | * |
54 | | * @upl: UPL state |
55 | | * @node: Node to read from |
56 | | * @prop: Property name to read |
57 | | * @addr: Place to put the size |
58 | | * Return: 0 if OK, -ve on error |
59 | | */ |
60 | | static int read_size(const struct upl *upl, ofnode node, const char *prop, |
61 | | ulong *sizep) |
62 | 0 | { |
63 | 0 | int ret; |
64 | |
|
65 | 0 | if (upl->size_cells == 1) { |
66 | 0 | u32 val; |
67 | |
|
68 | 0 | ret = ofnode_read_u32(node, prop, &val); |
69 | 0 | if (!ret) |
70 | 0 | *sizep = val; |
71 | 0 | } else { |
72 | 0 | u64 val; |
73 | |
|
74 | 0 | ret = ofnode_read_u64(node, prop, &val); |
75 | 0 | if (!ret) |
76 | 0 | *sizep = val; |
77 | 0 | } |
78 | |
|
79 | 0 | return ret; |
80 | 0 | } |
81 | | |
82 | | /** |
83 | | * ofnode_read_bitmask() - Read a bit mask from a string list |
84 | | * |
85 | | * @node: Node to read from |
86 | | * @prop: Property name to read |
87 | | * @names: Array of names for each bit |
88 | | * @count: Number of array entries |
89 | | * @value: Returns resulting bit-mask value on success |
90 | | * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOSPC if the |
91 | | * string is too long for the (internal) buffer, -EINVAL if no such property |
92 | | */ |
93 | | static int ofnode_read_bitmask(ofnode node, const char *prop, |
94 | | const char *const names[], uint count, |
95 | | uint *valuep) |
96 | 0 | { |
97 | 0 | const char **list; |
98 | 0 | const char **strp; |
99 | 0 | uint val; |
100 | 0 | uint bit; |
101 | 0 | int ret; |
102 | |
|
103 | 0 | ret = ofnode_read_string_list(node, prop, &list); |
104 | 0 | if (ret < 0) |
105 | 0 | return log_msg_ret("rea", ret); |
106 | | |
107 | 0 | val = 0; |
108 | 0 | for (strp = list; *strp; strp++) { |
109 | 0 | const char *str = *strp; |
110 | 0 | bool found = false; |
111 | |
|
112 | 0 | for (bit = 0; bit < count; bit++) { |
113 | 0 | if (!strcmp(str, names[bit])) { |
114 | 0 | found = true; |
115 | 0 | break; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | if (found) |
119 | 0 | val |= BIT(bit); |
120 | 0 | else |
121 | 0 | log_warning("%s/%s: Invalid value '%s'\n", |
122 | 0 | ofnode_get_name(node), prop, str); |
123 | 0 | } |
124 | 0 | *valuep = val; |
125 | |
|
126 | 0 | return 0; |
127 | 0 | } |
128 | | |
129 | | /** |
130 | | * ofnode_read_value() - Read a string value as an int using a lookup |
131 | | * |
132 | | * @node: Node to read from |
133 | | * @prop: Property name to read |
134 | | * @names: Array of names for each int value |
135 | | * @count: Number of array entries |
136 | | * @valuep: Returns int value read |
137 | | * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOENT if the |
138 | | * property does not exist |
139 | | */ |
140 | | static int ofnode_read_value(ofnode node, const char *prop, |
141 | | const char *const names[], uint count, |
142 | | uint *valuep) |
143 | 0 | { |
144 | 0 | const char *str; |
145 | 0 | int i; |
146 | |
|
147 | 0 | str = ofnode_read_string(node, prop); |
148 | 0 | if (!str) |
149 | 0 | return log_msg_ret("rd", -ENOENT); |
150 | | |
151 | 0 | for (i = 0; i < count; i++) { |
152 | 0 | if (!strcmp(names[i], str)) { |
153 | 0 | *valuep = i; |
154 | 0 | return 0; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | 0 | log_debug("Unnamed value '%s'\n", str); |
159 | 0 | return log_msg_ret("val", -EINVAL); |
160 | 0 | } |
161 | | |
162 | | static int read_uint(ofnode node, const char *prop, uint *valp) |
163 | 0 | { |
164 | 0 | u32 val; |
165 | 0 | int ret; |
166 | |
|
167 | 0 | ret = ofnode_read_u32(node, prop, &val); |
168 | 0 | if (ret) |
169 | 0 | return ret; |
170 | 0 | *valp = val; |
171 | |
|
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * decode_root_props() - Decode root properties from the tree |
177 | | * |
178 | | * @upl: UPL state |
179 | | * @node: Node to decode |
180 | | * Return 0 if OK, -ve on error |
181 | | */ |
182 | | static int decode_root_props(struct upl *upl, ofnode node) |
183 | 0 | { |
184 | 0 | int ret; |
185 | |
|
186 | 0 | ret = read_uint(node, UPLP_ADDRESS_CELLS, &upl->addr_cells); |
187 | 0 | if (!ret) |
188 | 0 | ret = read_uint(node, UPLP_SIZE_CELLS, &upl->size_cells); |
189 | 0 | if (ret) |
190 | 0 | return log_msg_ret("cel", ret); |
191 | | |
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * decode_root_props() - Decode UPL parameters from the tree |
197 | | * |
198 | | * @upl: UPL state |
199 | | * @node: Node to decode |
200 | | * Return 0 if OK, -ve on error |
201 | | */ |
202 | | static int decode_upl_params(struct upl *upl, ofnode options) |
203 | 0 | { |
204 | 0 | ofnode node; |
205 | 0 | int ret; |
206 | |
|
207 | 0 | node = ofnode_find_subnode(options, UPLN_UPL_PARAMS); |
208 | 0 | if (!ofnode_valid(node)) |
209 | 0 | return log_msg_ret("par", -EINVAL); |
210 | 0 | log_debug("decoding '%s'\n", ofnode_get_name(node)); |
211 | |
|
212 | 0 | ret = read_addr(upl, node, UPLP_SMBIOS, &upl->smbios); |
213 | 0 | if (ret) |
214 | 0 | return log_msg_ret("smb", ret); |
215 | 0 | ret = read_addr(upl, node, UPLP_ACPI, &upl->acpi); |
216 | 0 | if (ret) |
217 | 0 | return log_msg_ret("acp", ret); |
218 | 0 | ret = ofnode_read_bitmask(node, UPLP_BOOTMODE, bootmode_names, |
219 | 0 | UPLBM_COUNT, &upl->bootmode); |
220 | 0 | if (ret) |
221 | 0 | return log_msg_ret("boo", ret); |
222 | 0 | ret = read_uint(node, UPLP_ADDR_WIDTH, &upl->addr_width); |
223 | 0 | if (ret) |
224 | 0 | return log_msg_ret("add", ret); |
225 | 0 | ret = read_uint(node, UPLP_ACPI_NVS_SIZE, &upl->acpi_nvs_size); |
226 | 0 | if (ret) |
227 | 0 | return log_msg_ret("nvs", ret); |
228 | | |
229 | 0 | return 0; |
230 | 0 | } |
231 | | |
232 | | /** |
233 | | * decode_upl_images() - Decode /options/upl-image nodes |
234 | | * |
235 | | * @node: /options node in which to look for the node |
236 | | * Return 0 if OK, -ve on error |
237 | | */ |
238 | | static int decode_upl_images(struct upl *upl, ofnode options) |
239 | 0 | { |
240 | 0 | ofnode node, images; |
241 | 0 | int ret; |
242 | |
|
243 | 0 | images = ofnode_find_subnode(options, UPLN_UPL_IMAGE); |
244 | 0 | if (!ofnode_valid(images)) |
245 | 0 | return log_msg_ret("img", -EINVAL); |
246 | 0 | log_debug("decoding '%s'\n", ofnode_get_name(images)); |
247 | |
|
248 | 0 | ret = read_addr(upl, images, UPLP_FIT, &upl->fit); |
249 | 0 | if (!ret) |
250 | 0 | ret = read_uint(images, UPLP_CONF_OFFSET, &upl->conf_offset); |
251 | 0 | if (ret) |
252 | 0 | return log_msg_ret("cnf", ret); |
253 | | |
254 | 0 | ofnode_for_each_subnode(node, images) { |
255 | 0 | struct upl_image img; |
256 | |
|
257 | 0 | ret = read_addr(upl, node, UPLP_LOAD, &img.load); |
258 | 0 | if (!ret) |
259 | 0 | ret = read_size(upl, node, UPLP_SIZE, &img.size); |
260 | 0 | if (!ret) |
261 | 0 | ret = read_uint(node, UPLP_OFFSET, &img.offset); |
262 | 0 | img.description = ofnode_read_string(node, UPLP_DESCRIPTION); |
263 | 0 | if (!img.description) |
264 | 0 | return log_msg_ret("sim", ret); |
265 | 0 | if (!alist_add(&upl->image, img)) |
266 | 0 | return log_msg_ret("img", -ENOMEM); |
267 | 0 | } |
268 | | |
269 | 0 | return 0; |
270 | 0 | } |
271 | | |
272 | | /** |
273 | | * decode_addr_size() - Decide a set of addr/size pairs |
274 | | * |
275 | | * Each base/size value from the devicetree is written to the region list |
276 | | * |
277 | | * @upl: UPL state |
278 | | * @buf: Bytes to decode |
279 | | * @size: Number of bytes to decode |
280 | | * @regions: List of regions to process (struct memregion) |
281 | | * Returns: number of regions found, if OK, else -ve on error |
282 | | */ |
283 | | static int decode_addr_size(const struct upl *upl, const char *buf, int size, |
284 | | struct alist *regions) |
285 | 0 | { |
286 | 0 | const char *ptr, *end = buf + size; |
287 | 0 | int i; |
288 | |
|
289 | 0 | alist_init_struct(regions, struct memregion); |
290 | 0 | ptr = buf; |
291 | 0 | for (i = 0; ptr < end; i++) { |
292 | 0 | struct memregion reg; |
293 | |
|
294 | 0 | if (upl->addr_cells == 1) |
295 | 0 | reg.base = fdt32_to_cpu(*(u32 *)ptr); |
296 | 0 | else |
297 | 0 | reg.base = fdt64_to_cpu(*(u64 *)ptr); |
298 | 0 | ptr += upl->addr_cells * sizeof(u32); |
299 | |
|
300 | 0 | if (upl->size_cells == 1) |
301 | 0 | reg.size = fdt32_to_cpu(*(u32 *)ptr); |
302 | 0 | else |
303 | 0 | reg.size = fdt64_to_cpu(*(u64 *)ptr); |
304 | 0 | ptr += upl->size_cells * sizeof(u32); |
305 | 0 | if (ptr > end) |
306 | 0 | return -ENOSPC; |
307 | | |
308 | 0 | if (!alist_add(regions, reg)) |
309 | 0 | return log_msg_ret("reg", -ENOMEM); |
310 | 0 | } |
311 | | |
312 | 0 | return i; |
313 | 0 | } |
314 | | |
315 | | /** |
316 | | * node_matches_at() - Check if a node name matches "base@..." |
317 | | * |
318 | | * Return: true if the node name matches the base string followed by an @ sign; |
319 | | * false otherwise |
320 | | */ |
321 | | static bool node_matches_at(ofnode node, const char *base) |
322 | 0 | { |
323 | 0 | const char *name = ofnode_get_name(node); |
324 | 0 | int len = strlen(base); |
325 | |
|
326 | 0 | return !strncmp(base, name, len) && name[len] == '@'; |
327 | 0 | } |
328 | | |
329 | | /** |
330 | | * decode_upl_memory_node() - Decode a /memory node from the tree |
331 | | * |
332 | | * @upl: UPL state |
333 | | * @node: Node to decode |
334 | | * Return 0 if OK, -ve on error |
335 | | */ |
336 | | static int decode_upl_memory_node(struct upl *upl, ofnode node) |
337 | 0 | { |
338 | 0 | struct upl_mem mem; |
339 | 0 | const char *buf; |
340 | 0 | int size, len; |
341 | |
|
342 | 0 | buf = ofnode_read_prop(node, UPLP_REG, &size); |
343 | 0 | if (!buf) { |
344 | 0 | log_warning("Node '%s': Missing '%s' property\n", |
345 | 0 | ofnode_get_name(node), UPLP_REG); |
346 | 0 | return log_msg_ret("reg", -EINVAL); |
347 | 0 | } |
348 | 0 | len = decode_addr_size(upl, buf, size, &mem.region); |
349 | 0 | if (len < 0) |
350 | 0 | return log_msg_ret("buf", len); |
351 | 0 | mem.hotpluggable = ofnode_read_bool(node, UPLP_HOTPLUGGABLE); |
352 | 0 | if (!alist_add(&upl->mem, mem)) |
353 | 0 | return log_msg_ret("mem", -ENOMEM); |
354 | | |
355 | 0 | return 0; |
356 | 0 | } |
357 | | |
358 | | /** |
359 | | * decode_upl_memmap() - Decode memory-map nodes from the tree |
360 | | * |
361 | | * @upl: UPL state |
362 | | * @root: Parent node containing the /memory-map nodes |
363 | | * Return 0 if OK, -ve on error |
364 | | */ |
365 | | static int decode_upl_memmap(struct upl *upl, ofnode root) |
366 | 0 | { |
367 | 0 | ofnode node; |
368 | |
|
369 | 0 | ofnode_for_each_subnode(node, root) { |
370 | 0 | struct upl_memmap memmap; |
371 | 0 | int size, len, ret; |
372 | 0 | const char *buf; |
373 | |
|
374 | 0 | memmap.name = ofnode_get_name(node); |
375 | 0 | memmap.usage = 0; |
376 | |
|
377 | 0 | buf = ofnode_read_prop(node, UPLP_REG, &size); |
378 | 0 | if (!buf) { |
379 | 0 | log_warning("Node '%s': Missing '%s' property\n", |
380 | 0 | ofnode_get_name(node), UPLP_REG); |
381 | 0 | continue; |
382 | 0 | } |
383 | | |
384 | 0 | len = decode_addr_size(upl, buf, size, &memmap.region); |
385 | 0 | if (len < 0) |
386 | 0 | return log_msg_ret("buf", len); |
387 | 0 | ret = ofnode_read_bitmask(node, UPLP_USAGE, usage_names, |
388 | 0 | UPLUS_COUNT, &memmap.usage); |
389 | 0 | if (ret && ret != -EINVAL) /* optional property */ |
390 | 0 | return log_msg_ret("bit", ret); |
391 | | |
392 | 0 | if (!alist_add(&upl->memmap, memmap)) |
393 | 0 | return log_msg_ret("mmp", -ENOMEM); |
394 | 0 | } |
395 | | |
396 | 0 | return 0; |
397 | 0 | } |
398 | | |
399 | | /** |
400 | | * decode_upl_memres() - Decode reserved-memory nodes from the tree |
401 | | * |
402 | | * @upl: UPL state |
403 | | * @root: Parent node containing the reserved-memory nodes |
404 | | * Return 0 if OK, -ve on error |
405 | | */ |
406 | | static int decode_upl_memres(struct upl *upl, ofnode root) |
407 | 0 | { |
408 | 0 | ofnode node; |
409 | |
|
410 | 0 | ofnode_for_each_subnode(node, root) { |
411 | 0 | struct upl_memres memres; |
412 | 0 | const char *buf; |
413 | 0 | int size, len; |
414 | |
|
415 | 0 | log_debug("decoding '%s'\n", ofnode_get_name(node)); |
416 | 0 | memres.name = ofnode_get_name(node); |
417 | |
|
418 | 0 | buf = ofnode_read_prop(node, UPLP_REG, &size); |
419 | 0 | if (!buf) { |
420 | 0 | log_warning("Node '%s': Missing 'reg' property\n", |
421 | 0 | ofnode_get_name(node)); |
422 | 0 | continue; |
423 | 0 | } |
424 | | |
425 | 0 | len = decode_addr_size(upl, buf, size, &memres.region); |
426 | 0 | if (len < 0) |
427 | 0 | return log_msg_ret("buf", len); |
428 | 0 | memres.no_map = ofnode_read_bool(node, UPLP_NO_MAP); |
429 | |
|
430 | 0 | if (!alist_add(&upl->memres, memres)) |
431 | 0 | return log_msg_ret("mre", -ENOMEM); |
432 | 0 | } |
433 | | |
434 | 0 | return 0; |
435 | 0 | } |
436 | | |
437 | | /** |
438 | | * decode_upl_serial() - Decode the serial node |
439 | | * |
440 | | * @upl: UPL state |
441 | | * @root: Parent node contain node |
442 | | * Return 0 if OK, -ve on error |
443 | | */ |
444 | | static int decode_upl_serial(struct upl *upl, ofnode node) |
445 | 0 | { |
446 | 0 | struct upl_serial *ser = &upl->serial; |
447 | 0 | const char *buf; |
448 | 0 | int len, size; |
449 | 0 | int ret; |
450 | |
|
451 | 0 | ser->compatible = ofnode_read_string(node, UPLP_COMPATIBLE); |
452 | 0 | if (!ser->compatible) { |
453 | 0 | log_warning("Node '%s': Missing compatible string\n", |
454 | 0 | ofnode_get_name(node)); |
455 | 0 | return log_msg_ret("com", -EINVAL); |
456 | 0 | } |
457 | 0 | ret = read_uint(node, UPLP_CLOCK_FREQUENCY, &ser->clock_frequency); |
458 | 0 | if (!ret) |
459 | 0 | ret = read_uint(node, UPLP_CURRENT_SPEED, &ser->current_speed); |
460 | 0 | if (ret) |
461 | 0 | return log_msg_ret("spe", ret); |
462 | | |
463 | 0 | buf = ofnode_read_prop(node, UPLP_REG, &size); |
464 | 0 | if (!buf) { |
465 | 0 | log_warning("Node '%s': Missing 'reg' property\n", |
466 | 0 | ofnode_get_name(node)); |
467 | 0 | return log_msg_ret("reg", -EINVAL); |
468 | 0 | } |
469 | | |
470 | 0 | len = decode_addr_size(upl, buf, sizeof(buf), &ser->reg); |
471 | 0 | if (len < 0) |
472 | 0 | return log_msg_ret("buf", len); |
473 | | |
474 | | /* set defaults */ |
475 | 0 | ser->reg_io_shift = UPLD_REG_IO_SHIFT; |
476 | 0 | ser->reg_offset = UPLD_REG_OFFSET; |
477 | 0 | ser->reg_io_width = UPLD_REG_IO_WIDTH; |
478 | 0 | read_uint(node, UPLP_REG_IO_SHIFT, &ser->reg_io_shift); |
479 | 0 | read_uint(node, UPLP_REG_OFFSET, &ser->reg_offset); |
480 | 0 | read_uint(node, UPLP_REG_IO_WIDTH, &ser->reg_io_width); |
481 | 0 | read_addr(upl, node, UPLP_VIRTUAL_REG, &ser->virtual_reg); |
482 | 0 | ret = ofnode_read_value(node, UPLP_ACCESS_TYPE, access_types, |
483 | 0 | ARRAY_SIZE(access_types), &ser->access_type); |
484 | 0 | if (ret && ret != -ENOENT) |
485 | 0 | return log_msg_ret("ser", ret); |
486 | | |
487 | 0 | return 0; |
488 | 0 | } |
489 | | |
490 | | /** |
491 | | * decode_upl_graphics() - Decode graphics node |
492 | | * |
493 | | * @upl: UPL state |
494 | | * @root: Node to decode |
495 | | * Return 0 if OK, -ve on error |
496 | | */ |
497 | | static int decode_upl_graphics(struct upl *upl, ofnode node) |
498 | 0 | { |
499 | 0 | struct upl_graphics *gra = &upl->graphics; |
500 | 0 | const char *buf, *compat; |
501 | 0 | int len, size; |
502 | 0 | int ret; |
503 | |
|
504 | 0 | compat = ofnode_read_string(node, UPLP_COMPATIBLE); |
505 | 0 | if (!compat) { |
506 | 0 | log_warning("Node '%s': Missing compatible string\n", |
507 | 0 | ofnode_get_name(node)); |
508 | 0 | return log_msg_ret("com", -EINVAL); |
509 | 0 | } |
510 | 0 | if (strcmp(UPLC_GRAPHICS, compat)) { |
511 | 0 | log_warning("Node '%s': Ignoring compatible '%s'\n", |
512 | 0 | ofnode_get_name(node), compat); |
513 | 0 | return 0; |
514 | 0 | } |
515 | | |
516 | 0 | buf = ofnode_read_prop(node, UPLP_REG, &size); |
517 | 0 | if (!buf) { |
518 | 0 | log_warning("Node '%s': Missing 'reg' property\n", |
519 | 0 | ofnode_get_name(node)); |
520 | 0 | return log_msg_ret("reg", -EINVAL); |
521 | 0 | } |
522 | | |
523 | 0 | len = decode_addr_size(upl, buf, size, &gra->reg); |
524 | 0 | if (len < 0) |
525 | 0 | return log_msg_ret("buf", len); |
526 | | |
527 | 0 | ret = read_uint(node, UPLP_WIDTH, &gra->width); |
528 | 0 | if (!ret) |
529 | 0 | ret = read_uint(node, UPLP_HEIGHT, &gra->height); |
530 | 0 | if (!ret) |
531 | 0 | ret = read_uint(node, UPLP_STRIDE, &gra->stride); |
532 | 0 | if (!ret) { |
533 | 0 | ret = ofnode_read_value(node, UPLP_GRAPHICS_FORMAT, |
534 | 0 | graphics_formats, |
535 | 0 | ARRAY_SIZE(graphics_formats), |
536 | 0 | &gra->format); |
537 | 0 | } |
538 | 0 | if (ret) |
539 | 0 | return log_msg_ret("pro", ret); |
540 | | |
541 | 0 | return 0; |
542 | 0 | } |
543 | | |
544 | | int upl_read_handoff(struct upl *upl, oftree tree) |
545 | 0 | { |
546 | 0 | ofnode root, node; |
547 | 0 | int ret; |
548 | |
|
549 | 0 | if (!oftree_valid(tree)) |
550 | 0 | return log_msg_ret("tre", -EINVAL); |
551 | | |
552 | 0 | root = oftree_root(tree); |
553 | |
|
554 | 0 | upl_init(upl); |
555 | 0 | ret = decode_root_props(upl, root); |
556 | 0 | if (ret) |
557 | 0 | return log_msg_ret("roo", ret); |
558 | | |
559 | 0 | ofnode_for_each_subnode(node, root) { |
560 | 0 | const char *name = ofnode_get_name(node); |
561 | |
|
562 | 0 | log_debug("decoding '%s'\n", name); |
563 | 0 | if (!strcmp(UPLN_OPTIONS, name)) { |
564 | 0 | ret = decode_upl_params(upl, node); |
565 | 0 | if (ret) |
566 | 0 | return log_msg_ret("opt", ret); |
567 | | |
568 | 0 | ret = decode_upl_images(upl, node); |
569 | 0 | } else if (node_matches_at(node, UPLN_MEMORY)) { |
570 | 0 | ret = decode_upl_memory_node(upl, node); |
571 | 0 | } else if (!strcmp(UPLN_MEMORY_MAP, name)) { |
572 | 0 | ret = decode_upl_memmap(upl, node); |
573 | 0 | } else if (!strcmp(UPLN_MEMORY_RESERVED, name)) { |
574 | 0 | ret = decode_upl_memres(upl, node); |
575 | 0 | } else if (node_matches_at(node, UPLN_SERIAL)) { |
576 | 0 | ret = decode_upl_serial(upl, node); |
577 | 0 | } else if (node_matches_at(node, UPLN_GRAPHICS)) { |
578 | 0 | ret = decode_upl_graphics(upl, node); |
579 | 0 | } else { |
580 | 0 | log_debug("Unknown node '%s'\n", name); |
581 | 0 | ret = 0; |
582 | 0 | } |
583 | 0 | if (ret) |
584 | 0 | return log_msg_ret("err", ret); |
585 | 0 | } |
586 | | |
587 | 0 | return 0; |
588 | 0 | } |