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