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