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 struct efi_boot_services *boottime;
17 static const char *fdt;
18 
19 /* This should be sufficent for */
20 #define BUFFERSIZE 0x100000
21 
22 static efi_guid_t fdt_guid = EFI_FDT_GUID;
23 
24 /*
25  * Convert FDT value to host endianness.
26  *
27  * @val		FDT value
28  * @return	converted value
29  */
30 static uint32_t f2h(fdt32_t val)
31 {
32 	char *buf = (char *)&val;
33 	char i;
34 
35 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
36 	/* Swap the bytes */
37 	i = buf[0]; buf[0] = buf[3]; buf[3] = i;
38 	i = buf[1]; buf[1] = buf[2]; buf[2] = i;
39 #endif
40 	return *(uint32_t *)buf;
41 }
42 
43 /*
44  * Return the value of a property of the FDT root node.
45  *
46  * @name	name of the property
47  * @return	value of the property
48  */
49 static char *get_property(const u16 *property)
50 {
51 	struct fdt_header *header = (struct fdt_header *)fdt;
52 	const fdt32_t *pos;
53 	const char *strings;
54 
55 	if (!header)
56 		return NULL;
57 
58 	if (f2h(header->magic) != FDT_MAGIC) {
59 		printf("Wrong magic\n");
60 		return NULL;
61 	}
62 
63 	pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
64 	strings = fdt + f2h(header->off_dt_strings);
65 
66 	for (;;) {
67 		switch (f2h(pos[0])) {
68 		case FDT_BEGIN_NODE: {
69 			char *c = (char *)&pos[1];
70 			size_t i;
71 
72 			for (i = 0; c[i]; ++i)
73 				;
74 			pos = &pos[2 + (i >> 2)];
75 			break;
76 		}
77 		case FDT_PROP: {
78 			struct fdt_property *prop = (struct fdt_property *)pos;
79 			const char *label = &strings[f2h(prop->nameoff)];
80 			efi_status_t ret;
81 
82 			/* Check if this is the property to be returned */
83 			if (!efi_st_strcmp_16_8(property, label)) {
84 				char *str;
85 				efi_uintn_t len = f2h(prop->len);
86 
87 				if (!len)
88 					return NULL;
89 				/*
90 				 * The string might not be 0 terminated.
91 				 * It is safer to make a copy.
92 				 */
93 				ret = boottime->allocate_pool(
94 					EFI_LOADER_DATA, len + 1,
95 					(void **)&str);
96 				if (ret != EFI_SUCCESS) {
97 					efi_st_printf("AllocatePool failed\n");
98 					return NULL;
99 				}
100 				boottime->copy_mem(str, &pos[3], len);
101 				str[len] = 0;
102 
103 				return str;
104 			}
105 
106 			pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
107 			break;
108 		}
109 		case FDT_NOP:
110 			pos = &pos[1];
111 			break;
112 		default:
113 			return NULL;
114 		}
115 	}
116 }
117 
118 /*
119  * Setup unit test.
120  *
121  * @handle:	handle of the loaded image
122  * @systable:	system table
123  * @return:	EFI_ST_SUCCESS for success
124  */
125 static int setup(const efi_handle_t img_handle,
126 		 const struct efi_system_table *systable)
127 {
128 	efi_uintn_t i;
129 
130 	boottime = systable->boottime;
131 
132 	/* Find configuration tables */
133 	for (i = 0; i < systable->nr_tables; ++i) {
134 		if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
135 				   sizeof(efi_guid_t)))
136 			fdt = systable->tables[i].table;
137 	}
138 	if (!fdt) {
139 		efi_st_error("Missing device tree\n");
140 		return EFI_ST_FAILURE;
141 	}
142 
143 	return EFI_ST_SUCCESS;
144 }
145 
146 /*
147  * Execute unit test.
148  *
149  * @return:	EFI_ST_SUCCESS for success
150  */
151 static int execute(void)
152 {
153 	char *str;
154 	efi_status_t ret;
155 
156 	str = get_property(L"compatible");
157 	if (str) {
158 		efi_st_printf("compatible: %s\n", str);
159 		ret = boottime->free_pool(str);
160 		if (ret != EFI_SUCCESS) {
161 			efi_st_error("FreePool failed\n");
162 			return EFI_ST_FAILURE;
163 		}
164 	} else {
165 		efi_st_printf("Missing property 'compatible'\n");
166 		return EFI_ST_FAILURE;
167 	}
168 	str = get_property(L"serial-number");
169 	if (str) {
170 		efi_st_printf("serial-number: %s\n", str);
171 		ret = boottime->free_pool(str);
172 		if (ret != EFI_SUCCESS) {
173 			efi_st_error("FreePool failed\n");
174 			return EFI_ST_FAILURE;
175 		}
176 	}
177 
178 	return EFI_ST_SUCCESS;
179 }
180 
181 EFI_UNIT_TEST(fdt) = {
182 	.name = "device tree",
183 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
184 	.setup = setup,
185 	.execute = execute,
186 	.on_request = true,
187 };
188