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