/src/barebox/pbl/handoff-data.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0 |
2 | | #include <common.h> |
3 | | #include <pbl/handoff-data.h> |
4 | | #include <init.h> |
5 | | #include <linux/list.h> |
6 | | #include <memory.h> |
7 | | #include <fdt.h> |
8 | | #include <compressed-dtb.h> |
9 | | |
10 | | static struct handoff_data *handoff_data = (void *)-1; |
11 | | |
12 | | static struct handoff_data *handoff_data_get(void) |
13 | 52 | { |
14 | 52 | static struct handoff_data __handoff_data __section(.data); |
15 | | |
16 | | /* |
17 | | * Sometimes the PBL copies itself to some other location and is |
18 | | * re-entered at that location. For example on some i.MX SoCs we have |
19 | | * to move the PBL out of the SRAM (which will be occupied by the TF-A |
20 | | * later). We force the handoff_data variable into the data segment. |
21 | | * When moving the PBL somewhere else with handoff_data set we move the |
22 | | * content of the variable with it and thus find it to have the correct |
23 | | * value in the new PBL. |
24 | | */ |
25 | 52 | if (handoff_data == (void *)-1) { |
26 | 52 | handoff_data = &__handoff_data; |
27 | 52 | INIT_LIST_HEAD(&handoff_data->entries); |
28 | 52 | } |
29 | | |
30 | 52 | return handoff_data; |
31 | 52 | } |
32 | | |
33 | | /** |
34 | | * handoff_data_set - set the handoff data to be at a specified pointer |
35 | | * @handoff: the place where the handoff data is |
36 | | * |
37 | | * This sets the handoff data to @handoff. To be used by barebox proper |
38 | | * to pass the place where the handoff data has been placed by the PBL. |
39 | | */ |
40 | | void handoff_data_set(struct handoff_data *handoff) |
41 | 0 | { |
42 | 0 | handoff_data = handoff; |
43 | 0 | } |
44 | | |
45 | | /** |
46 | | * handoff_data_add_entry - add a new handoff data entry |
47 | | * @hde: the new entry |
48 | | * |
49 | | * This adds a new handoff data entry. |
50 | | */ |
51 | | void handoff_data_add_entry(struct handoff_data_entry *hde) |
52 | 0 | { |
53 | 0 | struct handoff_data *hd = handoff_data_get(); |
54 | |
|
55 | 0 | list_add_tail(&hde->list, &hd->entries); |
56 | 0 | } |
57 | | |
58 | | /** |
59 | | * handoff_data_size - calculate the handoff data size |
60 | | * |
61 | | * This calculates the size needed for the current handoff data |
62 | | * when put to a contiguous memory regions. Can be used to get the |
63 | | * size needed in preparation for a handoff_data_move() |
64 | | */ |
65 | | size_t __handoff_data_size(const struct handoff_data *hd) |
66 | 0 | { |
67 | 0 | struct handoff_data_entry *hde; |
68 | 0 | size_t size = 0; |
69 | 0 | size_t dsize = 0; |
70 | |
|
71 | 0 | if (!hd) |
72 | 0 | hd = handoff_data_get(); |
73 | |
|
74 | 0 | dsize += sizeof(*hd); |
75 | |
|
76 | 0 | list_for_each_entry(hde, &hd->entries, list) { |
77 | 0 | dsize += sizeof(*hde); |
78 | 0 | size += ALIGN(hde->size, 8); |
79 | 0 | } |
80 | |
|
81 | 0 | return dsize + size; |
82 | 0 | } |
83 | | |
84 | | /** |
85 | | * handoff_data_move - move handoff data to specified destination |
86 | | * @dest: The place where to move the handoff data to |
87 | | * |
88 | | * This moves the handoff data to @dest and also sets the new location |
89 | | * to @dest. This can be used to move the handoff data to a contiguous |
90 | | * region outside the binary. Note once moved no data should be added, |
91 | | * as that would make the handoff_data discontigoous again. |
92 | | */ |
93 | | void handoff_data_move(void *dest) |
94 | 0 | { |
95 | 0 | struct handoff_data *hd = handoff_data_get(); |
96 | 0 | struct handoff_data *hdnew = dest; |
97 | 0 | struct handoff_data_entry *hde; |
98 | |
|
99 | 0 | INIT_LIST_HEAD(&hdnew->entries); |
100 | |
|
101 | 0 | dest = hdnew + 1; |
102 | |
|
103 | 0 | list_for_each_entry(hde, &hd->entries, list) { |
104 | 0 | struct handoff_data_entry *newde = dest; |
105 | |
|
106 | 0 | dest = newde + 1; |
107 | |
|
108 | 0 | if (hde->flags & HANDOFF_DATA_FLAG_NO_COPY) { |
109 | 0 | newde->data = hde->data; |
110 | 0 | } else { |
111 | 0 | memcpy(dest, hde->data, hde->size); |
112 | 0 | newde->data = dest; |
113 | 0 | dest += ALIGN(hde->size, 8); |
114 | 0 | } |
115 | |
|
116 | 0 | newde->size = hde->size; |
117 | 0 | newde->cookie = hde->cookie; |
118 | 0 | newde->flags = hde->flags; |
119 | 0 | list_add_tail(&newde->list, &hdnew->entries); |
120 | 0 | } |
121 | |
|
122 | 0 | handoff_data_set(hdnew); |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * handoff_data_get_entry - get the memory associated to a cookie |
127 | | * @cookie: the cookie the data is identified with |
128 | | * @size: size of the memory returned |
129 | | * |
130 | | * This returns the memory associated with @cookie. |
131 | | */ |
132 | | void *handoff_data_get_entry(unsigned int cookie, size_t *size) |
133 | 0 | { |
134 | 0 | struct handoff_data *hd = handoff_data_get(); |
135 | 0 | struct handoff_data_entry *hde; |
136 | |
|
137 | 0 | list_for_each_entry(hde, &hd->entries, list) { |
138 | 0 | if (hde->cookie == cookie) { |
139 | 0 | *size = hde->size; |
140 | 0 | return hde->data; |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | 0 | return NULL; |
145 | 0 | } |
146 | | |
147 | | /** |
148 | | * handoff_data_show - show current handoff data entries |
149 | | * |
150 | | * This prints the current handoff data entries to the console for debugging |
151 | | * purposes. |
152 | | */ |
153 | | int handoff_data_show(void) |
154 | 0 | { |
155 | 0 | struct handoff_data *hd = handoff_data_get(); |
156 | 0 | struct handoff_data_entry *hde; |
157 | |
|
158 | 0 | list_for_each_entry(hde, &hd->entries, list) { |
159 | 0 | printf("handoff 0x%08x at 0x%p (size %zu)\n", |
160 | 0 | hde->cookie, hde->data, hde->size); |
161 | 0 | } |
162 | |
|
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | | void handoff_data_add_dt(void *fdt) |
167 | 0 | { |
168 | 0 | if (blob_is_fdt(fdt)) { |
169 | 0 | handoff_data_add(HANDOFF_DATA_INTERNAL_DT, fdt, |
170 | 0 | get_unaligned_be32(fdt + 4)); |
171 | 0 | } else if (blob_is_compressed_fdt(fdt)) { |
172 | 0 | struct barebox_boarddata_compressed_dtb *bd = fdt; |
173 | |
|
174 | 0 | handoff_data_add(HANDOFF_DATA_INTERNAL_DT_Z, fdt, |
175 | 0 | bd->datalen + sizeof(*bd)); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | static const char *handoff_data_entry_name(struct handoff_data_entry *hde) |
180 | 0 | { |
181 | 0 | static char name[sizeof("handoff 12345678")]; |
182 | |
|
183 | 0 | switch (hde->cookie) { |
184 | 0 | case HANDOFF_DATA_INTERNAL_DT: |
185 | 0 | return "handoff FDT (internal)"; |
186 | 0 | case HANDOFF_DATA_INTERNAL_DT_Z: |
187 | 0 | return "handoff FDT (internal, compressed)"; |
188 | 0 | case HANDOFF_DATA_EXTERNAL_DT: |
189 | 0 | return "handoff FDT (external)"; |
190 | 0 | case HANDOFF_DATA_ARM_MACHINE: |
191 | 0 | return "ARM machine number"; |
192 | 0 | case HANDOFF_DATA_TEE_DT_OVL: |
193 | 0 | return "handoff TEE FDTO"; |
194 | 0 | default: |
195 | 0 | sprintf(name, "handoff %08x", hde->cookie); |
196 | 0 | return name; |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | static int handoff_data_reserve(void) |
201 | 52 | { |
202 | 52 | struct handoff_data *hd = handoff_data_get(); |
203 | 52 | struct handoff_data_entry *hde; |
204 | | |
205 | 52 | list_for_each_entry(hde, &hd->entries, list) { |
206 | 0 | const char *name = handoff_data_entry_name(hde); |
207 | 0 | request_barebox_region(name, (resource_size_t)hde->data, hde->size, |
208 | 0 | MEMATTRS_RO); |
209 | 0 | } |
210 | | |
211 | 52 | return 0; |
212 | 52 | } |
213 | | late_initcall(handoff_data_reserve); |