]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
06c3d5b9 | 2 | /* |
b9b4cecf | 3 | * efi_selftest_fdt |
06c3d5b9 HS |
4 | * |
5 | * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> | |
b9b4cecf | 6 | * Copyright (c) 2022 Ventana Micro Systems Inc |
06c3d5b9 | 7 | * |
b9b4cecf | 8 | * Check the device tree, test the RISCV_EFI_BOOT_PROTOCOL. |
06c3d5b9 HS |
9 | */ |
10 | ||
b9b4cecf | 11 | #include <efi_riscv.h> |
06c3d5b9 | 12 | #include <efi_selftest.h> |
9967adb7 | 13 | #include <linux/libfdt.h> |
06c3d5b9 | 14 | |
68066d5b HS |
15 | static const struct efi_system_table *systemtab; |
16 | static const struct efi_boot_services *boottime; | |
06c3d5b9 HS |
17 | static const char *fdt; |
18 | ||
d8b2216c | 19 | /* This should be sufficient for */ |
06c3d5b9 HS |
20 | #define BUFFERSIZE 0x100000 |
21 | ||
68066d5b HS |
22 | static const efi_guid_t fdt_guid = EFI_FDT_GUID; |
23 | static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; | |
b9b4cecf HS |
24 | static const efi_guid_t riscv_efi_boot_protocol_guid = |
25 | RISCV_EFI_BOOT_PROTOCOL_GUID; | |
06c3d5b9 | 26 | |
9abd2ca9 HS |
27 | /** |
28 | * f2h() - convert FDT value to host endianness. | |
06c3d5b9 | 29 | * |
9abd2ca9 HS |
30 | * UEFI code is always low endian. The FDT is big endian. |
31 | * | |
32 | * @val: FDT value | |
33 | * Return: converted value | |
06c3d5b9 HS |
34 | */ |
35 | static uint32_t f2h(fdt32_t val) | |
36 | { | |
37 | char *buf = (char *)&val; | |
38 | char i; | |
39 | ||
06c3d5b9 HS |
40 | /* Swap the bytes */ |
41 | i = buf[0]; buf[0] = buf[3]; buf[3] = i; | |
42 | i = buf[1]; buf[1] = buf[2]; buf[2] = i; | |
9abd2ca9 HS |
43 | |
44 | return val; | |
06c3d5b9 HS |
45 | } |
46 | ||
18161a8a HS |
47 | /** |
48 | * get_property() - return value of a property of an FDT node | |
06c3d5b9 | 49 | * |
18161a8a HS |
50 | * A property of the root node or one of its direct children can be |
51 | * retrieved. | |
52 | * | |
53 | * @property name of the property | |
54 | * @node name of the node or NULL for root node | |
185f812c | 55 | * Return: value of the property |
06c3d5b9 | 56 | */ |
18161a8a | 57 | static char *get_property(const u16 *property, const u16 *node) |
06c3d5b9 HS |
58 | { |
59 | struct fdt_header *header = (struct fdt_header *)fdt; | |
18161a8a | 60 | const fdt32_t *end; |
06c3d5b9 HS |
61 | const fdt32_t *pos; |
62 | const char *strings; | |
18161a8a HS |
63 | size_t level = 0; |
64 | const char *nodelabel = NULL; | |
06c3d5b9 | 65 | |
18161a8a HS |
66 | if (!header) { |
67 | efi_st_error("Missing device tree\n"); | |
06c3d5b9 | 68 | return NULL; |
18161a8a | 69 | } |
06c3d5b9 HS |
70 | |
71 | if (f2h(header->magic) != FDT_MAGIC) { | |
18161a8a | 72 | efi_st_error("Wrong device tree magic\n"); |
06c3d5b9 HS |
73 | return NULL; |
74 | } | |
75 | ||
76 | pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct)); | |
18161a8a | 77 | end = &pos[f2h(header->totalsize) >> 2]; |
06c3d5b9 HS |
78 | strings = fdt + f2h(header->off_dt_strings); |
79 | ||
18161a8a | 80 | for (; pos < end;) { |
06c3d5b9 HS |
81 | switch (f2h(pos[0])) { |
82 | case FDT_BEGIN_NODE: { | |
18161a8a | 83 | const char *c = (char *)&pos[1]; |
06c3d5b9 HS |
84 | size_t i; |
85 | ||
18161a8a HS |
86 | if (level == 1) |
87 | nodelabel = c; | |
88 | ++level; | |
06c3d5b9 HS |
89 | for (i = 0; c[i]; ++i) |
90 | ; | |
91 | pos = &pos[2 + (i >> 2)]; | |
92 | break; | |
93 | } | |
94 | case FDT_PROP: { | |
95 | struct fdt_property *prop = (struct fdt_property *)pos; | |
96 | const char *label = &strings[f2h(prop->nameoff)]; | |
97 | efi_status_t ret; | |
98 | ||
99 | /* Check if this is the property to be returned */ | |
18161a8a HS |
100 | if (!efi_st_strcmp_16_8(property, label) && |
101 | ((level == 1 && !node) || | |
102 | (level == 2 && node && | |
103 | !efi_st_strcmp_16_8(node, nodelabel)))) { | |
06c3d5b9 HS |
104 | char *str; |
105 | efi_uintn_t len = f2h(prop->len); | |
106 | ||
107 | if (!len) | |
108 | return NULL; | |
109 | /* | |
110 | * The string might not be 0 terminated. | |
111 | * It is safer to make a copy. | |
112 | */ | |
113 | ret = boottime->allocate_pool( | |
114 | EFI_LOADER_DATA, len + 1, | |
115 | (void **)&str); | |
116 | if (ret != EFI_SUCCESS) { | |
18161a8a | 117 | efi_st_error("AllocatePool failed\n"); |
06c3d5b9 HS |
118 | return NULL; |
119 | } | |
120 | boottime->copy_mem(str, &pos[3], len); | |
121 | str[len] = 0; | |
122 | ||
123 | return str; | |
124 | } | |
125 | ||
126 | pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)]; | |
127 | break; | |
128 | } | |
129 | case FDT_NOP: | |
18161a8a HS |
130 | ++pos; |
131 | break; | |
132 | case FDT_END_NODE: | |
133 | --level; | |
134 | ++pos; | |
06c3d5b9 | 135 | break; |
18161a8a HS |
136 | case FDT_END: |
137 | return NULL; | |
06c3d5b9 | 138 | default: |
18161a8a | 139 | efi_st_error("Invalid device tree token\n"); |
06c3d5b9 HS |
140 | return NULL; |
141 | } | |
142 | } | |
18161a8a HS |
143 | efi_st_error("Missing FDT_END token\n"); |
144 | return NULL; | |
06c3d5b9 HS |
145 | } |
146 | ||
147 | /* | |
148 | * Setup unit test. | |
149 | * | |
150 | * @handle: handle of the loaded image | |
151 | * @systable: system table | |
3dd719d4 | 152 | * Return: EFI_ST_SUCCESS for success |
06c3d5b9 HS |
153 | */ |
154 | static int setup(const efi_handle_t img_handle, | |
155 | const struct efi_system_table *systable) | |
156 | { | |
68066d5b | 157 | void *acpi; |
06c3d5b9 | 158 | |
68066d5b | 159 | systemtab = systable; |
06c3d5b9 HS |
160 | boottime = systable->boottime; |
161 | ||
68066d5b HS |
162 | acpi = efi_st_get_config_table(&acpi_guid); |
163 | fdt = efi_st_get_config_table(&fdt_guid); | |
164 | ||
06c3d5b9 HS |
165 | if (!fdt) { |
166 | efi_st_error("Missing device tree\n"); | |
167 | return EFI_ST_FAILURE; | |
168 | } | |
68066d5b HS |
169 | if (acpi) { |
170 | efi_st_error("Found ACPI table and device tree\n"); | |
171 | return EFI_ST_FAILURE; | |
172 | } | |
06c3d5b9 HS |
173 | return EFI_ST_SUCCESS; |
174 | } | |
175 | ||
b9b4cecf HS |
176 | __maybe_unused static efi_status_t get_boot_hartid(efi_uintn_t *efi_hartid) |
177 | { | |
178 | efi_status_t ret; | |
179 | struct riscv_efi_boot_protocol *prot; | |
180 | ||
181 | /* Get RISC-V boot protocol */ | |
182 | ret = boottime->locate_protocol(&riscv_efi_boot_protocol_guid, NULL, | |
183 | (void **)&prot); | |
184 | if (ret != EFI_SUCCESS) { | |
185 | efi_st_error("RISC-V Boot Protocol not available\n"); | |
186 | return EFI_ST_FAILURE; | |
187 | } | |
188 | ||
189 | /* Get boot hart ID from EFI protocol */ | |
190 | ret = prot->get_boot_hartid(prot, efi_hartid); | |
191 | if (ret != EFI_SUCCESS) { | |
192 | efi_st_error("Could not retrieve boot hart ID\n"); | |
193 | return EFI_ST_FAILURE; | |
194 | } | |
195 | ||
196 | return EFI_ST_SUCCESS; | |
197 | } | |
198 | ||
06c3d5b9 HS |
199 | /* |
200 | * Execute unit test. | |
201 | * | |
3dd719d4 | 202 | * Return: EFI_ST_SUCCESS for success |
06c3d5b9 HS |
203 | */ |
204 | static int execute(void) | |
205 | { | |
206 | char *str; | |
207 | efi_status_t ret; | |
208 | ||
156ccbc3 | 209 | str = get_property(u"compatible", NULL); |
06c3d5b9 HS |
210 | if (str) { |
211 | efi_st_printf("compatible: %s\n", str); | |
212 | ret = boottime->free_pool(str); | |
213 | if (ret != EFI_SUCCESS) { | |
214 | efi_st_error("FreePool failed\n"); | |
215 | return EFI_ST_FAILURE; | |
216 | } | |
217 | } else { | |
18161a8a | 218 | efi_st_error("Missing property 'compatible'\n"); |
06c3d5b9 HS |
219 | return EFI_ST_FAILURE; |
220 | } | |
156ccbc3 | 221 | str = get_property(u"serial-number", NULL); |
06c3d5b9 HS |
222 | if (str) { |
223 | efi_st_printf("serial-number: %s\n", str); | |
224 | ret = boottime->free_pool(str); | |
225 | if (ret != EFI_SUCCESS) { | |
226 | efi_st_error("FreePool failed\n"); | |
227 | return EFI_ST_FAILURE; | |
228 | } | |
229 | } | |
52a84818 | 230 | if (IS_ENABLED(CONFIG_RISCV)) { |
b9b4cecf HS |
231 | u32 fdt_hartid; |
232 | ||
233 | str = get_property(u"boot-hartid", u"chosen"); | |
234 | if (!str) { | |
235 | efi_st_error("boot-hartid missing in devicetree\n"); | |
236 | return EFI_ST_FAILURE; | |
237 | } | |
238 | fdt_hartid = f2h(*(fdt32_t *)str); | |
239 | efi_st_printf("boot-hartid: %u\n", fdt_hartid); | |
240 | ||
241 | ret = boottime->free_pool(str); | |
242 | if (ret != EFI_SUCCESS) { | |
243 | efi_st_error("FreePool failed\n"); | |
244 | return EFI_ST_FAILURE; | |
245 | } | |
246 | ||
247 | if (IS_ENABLED(CONFIG_EFI_RISCV_BOOT_PROTOCOL)) { | |
248 | efi_uintn_t efi_hartid; | |
249 | int r; | |
250 | ||
251 | r = get_boot_hartid(&efi_hartid); | |
252 | if (r != EFI_ST_SUCCESS) | |
253 | return r; | |
254 | /* Boot hart ID should be same */ | |
255 | if (efi_hartid != fdt_hartid) { | |
256 | efi_st_error("boot-hartid differs: prot 0x%p, DT 0x%.8x\n", | |
257 | (void *)(uintptr_t)efi_hartid, | |
258 | fdt_hartid); | |
52a84818 HS |
259 | return EFI_ST_FAILURE; |
260 | } | |
52a84818 HS |
261 | } |
262 | } | |
06c3d5b9 HS |
263 | |
264 | return EFI_ST_SUCCESS; | |
265 | } | |
266 | ||
267 | EFI_UNIT_TEST(fdt) = { | |
268 | .name = "device tree", | |
269 | .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, | |
270 | .setup = setup, | |
271 | .execute = execute, | |
06c3d5b9 | 272 | }; |