1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Hello world EFI application
4 *
5 * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
6 *
7 * This test program is used to test the invocation of an EFI application.
8 * It writes
9 *
10 * * a greeting
11 * * the firmware's UEFI version
12 * * the installed configuration tables
13 * * the boot device's device path and the file path
14 *
15 * to the console.
16 */
17
18#include <efi_api.h>
19
20static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
21static const efi_guid_t device_path_to_text_protocol_guid =
22	EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
23static const efi_guid_t device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
24static const efi_guid_t fdt_guid = EFI_FDT_GUID;
25static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
26static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
27
28static struct efi_system_table *systable;
29static struct efi_boot_services *boottime;
30static struct efi_simple_text_output_protocol *con_out;
31
32/*
33 * Print an unsigned 32bit value as decimal number to an u16 string
34 *
35 * @value:	value to be printed
36 * @buf:	pointer to buffer address
37 *		on return position of terminating zero word
38 */
39static void uint2dec(u32 value, u16 **buf)
40{
41	u16 *pos = *buf;
42	int i;
43	u16 c;
44	u64 f;
45
46	/*
47	 * Increment by .5 and multiply with
48	 * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
49	 * to move the first digit to bit 60-63.
50	 */
51	f = 0x225C17D0;
52	f += (0x9B5A52DULL * value) >> 28;
53	f += 0x44B82FA0ULL * value;
54
55	for (i = 0; i < 10; ++i) {
56		/* Write current digit */
57		c = f >> 60;
58		if (c || pos != *buf)
59			*pos++ = c + '0';
60		/* Eliminate current digit */
61		f &= 0xfffffffffffffff;
62		/* Get next digit */
63		f *= 0xaULL;
64	}
65	if (pos == *buf)
66		*pos++ = '0';
67	*pos = 0;
68	*buf = pos;
69}
70
71/**
72 * print_uefi_revision() - print UEFI revision number
73 */
74static void print_uefi_revision(void)
75{
76	u16 rev[13] = {0};
77	u16 *buf = rev;
78	u16 digit;
79
80	uint2dec(systable->hdr.revision >> 16, &buf);
81	*buf++ = '.';
82	uint2dec(systable->hdr.revision & 0xffff, &buf);
83
84	/* Minor revision is only to be shown if non-zero */
85	digit = *--buf;
86	if (digit == '0') {
87		*buf = 0;
88	} else {
89		*buf++ = '.';
90		*buf = digit;
91	}
92
93	con_out->output_string(con_out, u"Running on UEFI ");
94	con_out->output_string(con_out, rev);
95	con_out->output_string(con_out, u"\r\n");
96}
97
98/**
99 * print_config_tables() - print configuration tables
100 */
101static void print_config_tables(void)
102{
103	efi_uintn_t i;
104
105	/* Find configuration tables */
106	for (i = 0; i < systable->nr_tables; ++i) {
107		if (!memcmp(&systable->tables[i].guid, &fdt_guid,
108			    sizeof(efi_guid_t)))
109			con_out->output_string
110					(con_out, u"Have device tree\r\n");
111		if (!memcmp(&systable->tables[i].guid, &acpi_guid,
112			    sizeof(efi_guid_t)))
113			con_out->output_string
114					(con_out, u"Have ACPI 2.0 table\r\n");
115		if (!memcmp(&systable->tables[i].guid, &smbios_guid,
116			    sizeof(efi_guid_t)))
117			con_out->output_string
118					(con_out, u"Have SMBIOS table\r\n");
119	}
120}
121
122/**
123 * print_load_options() - print load options
124 *
125 * @systable:	system table
126 * @con_out:	simple text output protocol
127 */
128static void print_load_options(struct efi_loaded_image *loaded_image)
129{
130	/* Output the load options */
131	con_out->output_string(con_out, u"Load options: ");
132	if (loaded_image->load_options_size && loaded_image->load_options)
133		con_out->output_string(con_out,
134				       (u16 *)loaded_image->load_options);
135	else
136		con_out->output_string(con_out, u"<none>");
137	con_out->output_string(con_out, u"\r\n");
138}
139
140/**
141 * print_device_path() - print device path
142 *
143 * @device_path:	device path to print
144 * @dp2txt:		device path to text protocol
145 */
146static
147efi_status_t print_device_path(struct efi_device_path *device_path,
148			       struct efi_device_path_to_text_protocol *dp2txt)
149{
150	u16 *string;
151	efi_status_t ret;
152
153	if (!device_path) {
154		con_out->output_string(con_out, u"<none>\r\n");
155		return EFI_SUCCESS;
156	}
157
158	string = dp2txt->convert_device_path_to_text(device_path, true, false);
159	if (!string) {
160		con_out->output_string
161			(con_out, u"Cannot convert device path to text\r\n");
162		return EFI_OUT_OF_RESOURCES;
163	}
164	con_out->output_string(con_out, string);
165	con_out->output_string(con_out, u"\r\n");
166	ret = boottime->free_pool(string);
167	if (ret != EFI_SUCCESS) {
168		con_out->output_string(con_out, u"Cannot free pool memory\r\n");
169		return ret;
170	}
171	return EFI_SUCCESS;
172}
173
174/**
175 * efi_main() - entry point of the EFI application.
176 *
177 * @handle:	handle of the loaded image
178 * @systab:	system table
179 * Return:	status code
180 */
181efi_status_t EFIAPI efi_main(efi_handle_t handle,
182			     struct efi_system_table *systab)
183{
184	struct efi_loaded_image *loaded_image;
185	struct efi_device_path_to_text_protocol *device_path_to_text;
186	struct efi_device_path *device_path;
187	efi_status_t ret;
188
189	systable = systab;
190	boottime = systable->boottime;
191	con_out = systable->con_out;
192
193	/* UEFI requires CR LF */
194	con_out->output_string(con_out, u"Hello, world!\r\n");
195
196	print_uefi_revision();
197	print_config_tables();
198
199	/* Get the loaded image protocol */
200	ret = boottime->open_protocol(handle, &loaded_image_guid,
201				      (void **)&loaded_image, NULL, NULL,
202				      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
203
204	if (ret != EFI_SUCCESS) {
205		con_out->output_string
206			(con_out, u"Cannot open loaded image protocol\r\n");
207		goto out;
208	}
209	print_load_options(loaded_image);
210
211	/* Get the device path to text protocol */
212	ret = boottime->locate_protocol(&device_path_to_text_protocol_guid,
213					NULL, (void **)&device_path_to_text);
214	if (ret != EFI_SUCCESS) {
215		con_out->output_string
216			(con_out, u"Cannot open device path to text protocol\r\n");
217		goto out;
218	}
219	con_out->output_string(con_out, u"File path: ");
220	ret = print_device_path(loaded_image->file_path, device_path_to_text);
221	if (ret != EFI_SUCCESS)
222		goto out;
223	if (!loaded_image->device_handle) {
224		con_out->output_string
225			(con_out, u"Missing device handle\r\n");
226		goto out;
227	}
228	ret = boottime->open_protocol(loaded_image->device_handle,
229				      &device_path_guid,
230				      (void **)&device_path, NULL, NULL,
231				      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
232	if (ret != EFI_SUCCESS) {
233		con_out->output_string
234			(con_out, u"Missing device path for device handle\r\n");
235		goto out;
236	}
237	con_out->output_string(con_out, u"Boot device: ");
238	ret = print_device_path(device_path, device_path_to_text);
239	if (ret != EFI_SUCCESS)
240		goto out;
241
242out:
243	boottime->exit(handle, ret, 0, NULL);
244
245	/* We should never arrive here */
246	return ret;
247}
248