/src/barebox/common/state/backend_format_dtb.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012-2014 Pengutronix, Jan Luebbe <j.luebbe@pengutronix.de> |
3 | | * Copyright (C) 2013-2014 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> |
4 | | * Copyright (C) 2015 Pengutronix, Marc Kleine-Budde <mkl@pengutronix.de> |
5 | | * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de> |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU General Public License |
9 | | * version 2, as published by the Free Software Foundation. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | */ |
17 | | |
18 | | #include <common.h> |
19 | | #include <of.h> |
20 | | #include <linux/kernel.h> |
21 | | #include <malloc.h> |
22 | | |
23 | | #include "state.h" |
24 | | |
25 | | struct state_backend_format_dtb { |
26 | | struct state_backend_format format; |
27 | | |
28 | | struct device_node *root; |
29 | | |
30 | | /* For outputs */ |
31 | | struct device *dev; |
32 | | }; |
33 | | |
34 | | static inline struct state_backend_format_dtb *get_format_dtb(struct |
35 | | state_backend_format |
36 | | *format) |
37 | 0 | { |
38 | 0 | return container_of(format, struct state_backend_format_dtb, format); |
39 | 0 | } |
40 | | |
41 | | static int state_backend_format_dtb_verify(struct state_backend_format *format, |
42 | | uint32_t magic, const void * buf, |
43 | | ssize_t *lenp, enum state_flags flags) |
44 | 0 | { |
45 | 0 | struct state_backend_format_dtb *fdtb = get_format_dtb(format); |
46 | 0 | struct device_node *root; |
47 | 0 | struct fdt_header *fdt = (struct fdt_header *)buf; |
48 | 0 | size_t dtb_len; |
49 | 0 | size_t len = *lenp; |
50 | |
|
51 | 0 | if (len < sizeof(*fdt)) |
52 | 0 | return -EINVAL; |
53 | | |
54 | 0 | dtb_len = fdt32_to_cpu(fdt->totalsize); |
55 | 0 | if (dtb_len > len) { |
56 | 0 | dev_err(fdtb->dev, "Error, stored DTB length (%zd) longer than read buffer (%zd)\n", |
57 | 0 | dtb_len, len); |
58 | 0 | return -EINVAL; |
59 | 0 | } |
60 | | |
61 | 0 | if (fdtb->root) { |
62 | 0 | of_delete_node(fdtb->root); |
63 | 0 | fdtb->root = NULL; |
64 | 0 | } |
65 | |
|
66 | 0 | root = of_unflatten_dtb(buf, dtb_len); |
67 | 0 | if (IS_ERR(root)) { |
68 | 0 | dev_err(fdtb->dev, "Failed to unflatten dtb from buffer with length %zd, %ld\n", |
69 | 0 | len, PTR_ERR(root)); |
70 | 0 | return PTR_ERR(root); |
71 | 0 | } |
72 | | |
73 | 0 | fdtb->root = root; |
74 | |
|
75 | 0 | *lenp = be32_to_cpu(fdt->totalsize); |
76 | |
|
77 | 0 | return 0; |
78 | 0 | } |
79 | | |
80 | | static int state_backend_format_dtb_unpack(struct state_backend_format *format, |
81 | | struct state *state, |
82 | | const void * buf, ssize_t len) |
83 | 0 | { |
84 | 0 | struct state_backend_format_dtb *fdtb = get_format_dtb(format); |
85 | 0 | int ret; |
86 | |
|
87 | 0 | if (!fdtb->root) { |
88 | 0 | state_backend_format_dtb_verify(format, 0, buf, &len, 0); |
89 | 0 | } |
90 | |
|
91 | 0 | ret = state_from_node(state, fdtb->root, 0); |
92 | 0 | of_delete_node(fdtb->root); |
93 | 0 | fdtb->root = NULL; |
94 | |
|
95 | 0 | return ret; |
96 | 0 | } |
97 | | |
98 | | static int state_backend_format_dtb_pack(struct state_backend_format *format, |
99 | | struct state *state, void ** buf, |
100 | | ssize_t * len) |
101 | 0 | { |
102 | 0 | struct state_backend_format_dtb *fdtb = get_format_dtb(format); |
103 | 0 | struct device_node *root; |
104 | 0 | struct fdt_header *fdt; |
105 | |
|
106 | 0 | root = state_to_node(state, NULL, STATE_CONVERT_TO_NODE); |
107 | 0 | if (IS_ERR(root)) { |
108 | 0 | dev_err(fdtb->dev, "Failed to convert state to device node, %ld\n", |
109 | 0 | PTR_ERR(root)); |
110 | 0 | return PTR_ERR(root); |
111 | 0 | } |
112 | | |
113 | 0 | fdt = of_flatten_dtb(root); |
114 | 0 | if (!fdt) { |
115 | 0 | dev_err(fdtb->dev, "Failed to create flattened dtb\n"); |
116 | 0 | of_delete_node(root); |
117 | 0 | return -EINVAL; |
118 | 0 | } |
119 | | |
120 | 0 | *buf = (uint8_t *) fdt; |
121 | 0 | *len = fdt32_to_cpu(fdt->totalsize); |
122 | |
|
123 | 0 | if (fdtb->root) |
124 | 0 | of_delete_node(fdtb->root); |
125 | 0 | fdtb->root = root; |
126 | |
|
127 | 0 | return 0; |
128 | 0 | } |
129 | | |
130 | | static void state_backend_format_dtb_free(struct state_backend_format *format) |
131 | 0 | { |
132 | 0 | struct state_backend_format_dtb *fdtb = get_format_dtb(format); |
133 | |
|
134 | 0 | free(fdtb); |
135 | 0 | } |
136 | | |
137 | | int backend_format_dtb_create(struct state_backend_format **format, |
138 | | struct device *dev) |
139 | 0 | { |
140 | 0 | struct state_backend_format_dtb *dtb; |
141 | |
|
142 | 0 | dtb = xzalloc(sizeof(*dtb)); |
143 | |
|
144 | 0 | dtb->dev = dev; |
145 | 0 | dtb->format.pack = state_backend_format_dtb_pack; |
146 | 0 | dtb->format.unpack = state_backend_format_dtb_unpack; |
147 | 0 | dtb->format.verify = state_backend_format_dtb_verify; |
148 | 0 | dtb->format.free = state_backend_format_dtb_free; |
149 | 0 | dtb->format.name = "dtb"; |
150 | 0 | *format = &dtb->format; |
151 | |
|
152 | 0 | return 0; |
153 | 0 | } |