10eaa5176SVarad Gautam /*
20eaa5176SVarad Gautam * EFI-related functions to set up and run test cases in EFI
30eaa5176SVarad Gautam *
40eaa5176SVarad Gautam * Copyright (c) 2021, SUSE, Varad Gautam <varad.gautam@suse.com>
5ad5fb883SZixuan Wang * Copyright (c) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com>
60eaa5176SVarad Gautam *
70eaa5176SVarad Gautam * SPDX-License-Identifier: LGPL-2.0-or-later
80eaa5176SVarad Gautam */
9ad5fb883SZixuan Wang #include <libcflat.h>
109632ce44SAndrew Jones #include <argv.h>
119632ce44SAndrew Jones #include <ctype.h>
129632ce44SAndrew Jones #include <stdlib.h>
13ad5fb883SZixuan Wang #include <asm/setup.h>
149632ce44SAndrew Jones #include "efi.h"
15ad5fb883SZixuan Wang
16b9f8ff7cSAndrew Jones /* From each arch */
17b9f8ff7cSAndrew Jones extern char *initrd;
18b9f8ff7cSAndrew Jones extern u32 initrd_size;
19b9f8ff7cSAndrew Jones
20ad5fb883SZixuan Wang /* From lib/argv.c */
21ad5fb883SZixuan Wang extern int __argc, __envc;
22ad5fb883SZixuan Wang extern char *__argv[100];
23ad5fb883SZixuan Wang extern char *__environ[200];
24ad5fb883SZixuan Wang
2525ef5154SNadav Amit extern char _text;
2625ef5154SNadav Amit
27ad5fb883SZixuan Wang extern int main(int argc, char **argv, char **envp);
28ad5fb883SZixuan Wang
290eaa5176SVarad Gautam efi_system_table_t *efi_system_table = NULL;
300eaa5176SVarad Gautam
31ba0a6fd4SAndrew Jones #ifdef __riscv
32ba0a6fd4SAndrew Jones #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec, 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
33ba0a6fd4SAndrew Jones
34ba0a6fd4SAndrew Jones unsigned long boot_hartid;
35ba0a6fd4SAndrew Jones
36ba0a6fd4SAndrew Jones struct riscv_efi_boot_protocol {
37ba0a6fd4SAndrew Jones u64 revision;
38ba0a6fd4SAndrew Jones efi_status_t (*get_boot_hartid)(struct riscv_efi_boot_protocol *,
39ba0a6fd4SAndrew Jones unsigned long *boot_hartid);
40ba0a6fd4SAndrew Jones };
41ba0a6fd4SAndrew Jones
efi_get_boot_hartid(void)42ba0a6fd4SAndrew Jones static efi_status_t efi_get_boot_hartid(void)
43ba0a6fd4SAndrew Jones {
44ba0a6fd4SAndrew Jones efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
45ba0a6fd4SAndrew Jones struct riscv_efi_boot_protocol *boot_protocol;
46ba0a6fd4SAndrew Jones efi_status_t status;
47ba0a6fd4SAndrew Jones
48ba0a6fd4SAndrew Jones status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
49ba0a6fd4SAndrew Jones (void **)&boot_protocol);
50ba0a6fd4SAndrew Jones if (status != EFI_SUCCESS)
51ba0a6fd4SAndrew Jones return status;
52ba0a6fd4SAndrew Jones return efi_call_proto(boot_protocol, get_boot_hartid, &boot_hartid);
53ba0a6fd4SAndrew Jones }
54ba0a6fd4SAndrew Jones #endif
55ba0a6fd4SAndrew Jones
efi_free_pool(void * ptr)560eaa5176SVarad Gautam static void efi_free_pool(void *ptr)
570eaa5176SVarad Gautam {
580eaa5176SVarad Gautam efi_bs_call(free_pool, ptr);
590eaa5176SVarad Gautam }
600eaa5176SVarad Gautam
efi_get_memory_map(struct efi_boot_memmap * map)61ad5fb883SZixuan Wang efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
620eaa5176SVarad Gautam {
630eaa5176SVarad Gautam efi_memory_desc_t *m = NULL;
640eaa5176SVarad Gautam efi_status_t status;
650eaa5176SVarad Gautam unsigned long key = 0, map_size = 0, desc_size = 0;
661ae9072eSZixuan Wang u32 desc_ver;
670eaa5176SVarad Gautam
680eaa5176SVarad Gautam status = efi_bs_call(get_memory_map, &map_size,
691ae9072eSZixuan Wang NULL, &key, &desc_size, &desc_ver);
700eaa5176SVarad Gautam if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
710eaa5176SVarad Gautam goto out;
720eaa5176SVarad Gautam
730eaa5176SVarad Gautam /*
740eaa5176SVarad Gautam * Pad map_size with additional descriptors so we don't need to
750eaa5176SVarad Gautam * retry.
760eaa5176SVarad Gautam */
770eaa5176SVarad Gautam map_size += 4 * desc_size;
780eaa5176SVarad Gautam *map->buff_size = map_size;
790eaa5176SVarad Gautam status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
800eaa5176SVarad Gautam map_size, (void **)&m);
810eaa5176SVarad Gautam if (status != EFI_SUCCESS)
820eaa5176SVarad Gautam goto out;
830eaa5176SVarad Gautam
840eaa5176SVarad Gautam /* Get the map. */
850eaa5176SVarad Gautam status = efi_bs_call(get_memory_map, &map_size,
861ae9072eSZixuan Wang m, &key, &desc_size, &desc_ver);
870eaa5176SVarad Gautam if (status != EFI_SUCCESS) {
880eaa5176SVarad Gautam efi_free_pool(m);
890eaa5176SVarad Gautam goto out;
900eaa5176SVarad Gautam }
910eaa5176SVarad Gautam
921ae9072eSZixuan Wang *map->desc_ver = desc_ver;
930eaa5176SVarad Gautam *map->desc_size = desc_size;
940eaa5176SVarad Gautam *map->map_size = map_size;
950eaa5176SVarad Gautam *map->key_ptr = key;
960eaa5176SVarad Gautam out:
970eaa5176SVarad Gautam *map->map = m;
980eaa5176SVarad Gautam return status;
990eaa5176SVarad Gautam }
1000eaa5176SVarad Gautam
efi_exit_boot_services(void * handle,struct efi_boot_memmap * map)101b4e8c300SZixuan Wang efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
1020eaa5176SVarad Gautam {
103b4e8c300SZixuan Wang return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
1040eaa5176SVarad Gautam }
1050eaa5176SVarad Gautam
efi_get_system_config_table(efi_guid_t table_guid,void ** table)106f20589d6SZixuan Wang efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
107f20589d6SZixuan Wang {
108f20589d6SZixuan Wang size_t i;
109f20589d6SZixuan Wang efi_config_table_t *tables;
110f20589d6SZixuan Wang
111f20589d6SZixuan Wang tables = (efi_config_table_t *)efi_system_table->tables;
112f20589d6SZixuan Wang for (i = 0; i < efi_system_table->nr_tables; i++) {
113f20589d6SZixuan Wang if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
114f20589d6SZixuan Wang *table = tables[i].table;
115f20589d6SZixuan Wang return EFI_SUCCESS;
116f20589d6SZixuan Wang }
117f20589d6SZixuan Wang }
118f20589d6SZixuan Wang return EFI_NOT_FOUND;
119f20589d6SZixuan Wang }
120f20589d6SZixuan Wang
efi_exit(efi_status_t code)1212f47d025SZixuan Wang static void efi_exit(efi_status_t code)
1222f47d025SZixuan Wang {
1232f47d025SZixuan Wang exit(code);
1242f47d025SZixuan Wang
1252f47d025SZixuan Wang /*
1262f47d025SZixuan Wang * Fallback to UEFI reset_system() service, in case testdev is
1272f47d025SZixuan Wang * missing and exit() does not properly exit.
1282f47d025SZixuan Wang */
1292f47d025SZixuan Wang efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
1302f47d025SZixuan Wang }
1312f47d025SZixuan Wang
13285c3c524SNikos Nikoleris /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */
efi_convert_cmdline(struct efi_loaded_image_64 * image,int * cmd_line_len)13385c3c524SNikos Nikoleris static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len)
13485c3c524SNikos Nikoleris {
13585c3c524SNikos Nikoleris const u16 *s2;
13685c3c524SNikos Nikoleris unsigned long cmdline_addr = 0;
13785c3c524SNikos Nikoleris int options_chars = image->load_options_size;
13885c3c524SNikos Nikoleris const u16 *options = image->load_options;
13985c3c524SNikos Nikoleris int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
14085c3c524SNikos Nikoleris bool in_quote = false;
14185c3c524SNikos Nikoleris efi_status_t status;
14285c3c524SNikos Nikoleris const int COMMAND_LINE_SIZE = 2048;
14385c3c524SNikos Nikoleris
14485c3c524SNikos Nikoleris if (options) {
14585c3c524SNikos Nikoleris s2 = options;
14685c3c524SNikos Nikoleris while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
14785c3c524SNikos Nikoleris u16 c = *s2++;
14885c3c524SNikos Nikoleris
14985c3c524SNikos Nikoleris if (c < 0x80) {
15085c3c524SNikos Nikoleris if (c == L'\0' || c == L'\n')
15185c3c524SNikos Nikoleris break;
15285c3c524SNikos Nikoleris if (c == L'"')
15385c3c524SNikos Nikoleris in_quote = !in_quote;
15485c3c524SNikos Nikoleris else if (!in_quote && isspace((char)c))
15585c3c524SNikos Nikoleris safe_options_bytes = options_bytes;
15685c3c524SNikos Nikoleris
15785c3c524SNikos Nikoleris options_bytes++;
15885c3c524SNikos Nikoleris continue;
15985c3c524SNikos Nikoleris }
16085c3c524SNikos Nikoleris
16185c3c524SNikos Nikoleris /*
16285c3c524SNikos Nikoleris * Get the number of UTF-8 bytes corresponding to a
16385c3c524SNikos Nikoleris * UTF-16 character.
16485c3c524SNikos Nikoleris * The first part handles everything in the BMP.
16585c3c524SNikos Nikoleris */
16685c3c524SNikos Nikoleris options_bytes += 2 + (c >= 0x800);
16785c3c524SNikos Nikoleris /*
16885c3c524SNikos Nikoleris * Add one more byte for valid surrogate pairs. Invalid
16985c3c524SNikos Nikoleris * surrogates will be replaced with 0xfffd and take up
17085c3c524SNikos Nikoleris * only 3 bytes.
17185c3c524SNikos Nikoleris */
17285c3c524SNikos Nikoleris if ((c & 0xfc00) == 0xd800) {
17385c3c524SNikos Nikoleris /*
17485c3c524SNikos Nikoleris * If the very last word is a high surrogate,
17585c3c524SNikos Nikoleris * we must ignore it since we can't access the
17685c3c524SNikos Nikoleris * low surrogate.
17785c3c524SNikos Nikoleris */
17885c3c524SNikos Nikoleris if (!options_chars) {
17985c3c524SNikos Nikoleris options_bytes -= 3;
18085c3c524SNikos Nikoleris } else if ((*s2 & 0xfc00) == 0xdc00) {
18185c3c524SNikos Nikoleris options_bytes++;
18285c3c524SNikos Nikoleris options_chars--;
18385c3c524SNikos Nikoleris s2++;
18485c3c524SNikos Nikoleris }
18585c3c524SNikos Nikoleris }
18685c3c524SNikos Nikoleris }
18785c3c524SNikos Nikoleris if (options_bytes >= COMMAND_LINE_SIZE) {
18885c3c524SNikos Nikoleris options_bytes = safe_options_bytes;
18985c3c524SNikos Nikoleris printf("Command line is too long: truncated to %d bytes\n",
19085c3c524SNikos Nikoleris options_bytes);
19185c3c524SNikos Nikoleris }
19285c3c524SNikos Nikoleris }
19385c3c524SNikos Nikoleris
19485c3c524SNikos Nikoleris options_bytes++; /* NUL termination */
19585c3c524SNikos Nikoleris
19685c3c524SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr);
19785c3c524SNikos Nikoleris if (status != EFI_SUCCESS)
19885c3c524SNikos Nikoleris return NULL;
19985c3c524SNikos Nikoleris
20085c3c524SNikos Nikoleris snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options);
20185c3c524SNikos Nikoleris
20285c3c524SNikos Nikoleris *cmd_line_len = options_bytes;
20385c3c524SNikos Nikoleris return (char *)cmdline_addr;
20485c3c524SNikos Nikoleris }
20585c3c524SNikos Nikoleris
206bb294dfdSPavan Kumar Paluri #if defined(__aarch64__) || defined(__riscv)
207*fc35f6ccSVasant Karasulli #include "libfdt/libfdt.h"
208529ab889SNikos Nikoleris /*
209529ab889SNikos Nikoleris * Open the file and read it into a buffer.
210529ab889SNikos Nikoleris */
efi_load_image(efi_handle_t handle,struct efi_loaded_image_64 * image,void ** data,int * datasize,efi_char16_t * path_name)211529ab889SNikos Nikoleris static void efi_load_image(efi_handle_t handle, struct efi_loaded_image_64 *image, void **data,
212529ab889SNikos Nikoleris int *datasize, efi_char16_t *path_name)
213529ab889SNikos Nikoleris {
214529ab889SNikos Nikoleris uint64_t buffer_size = sizeof(efi_file_info_t);
215529ab889SNikos Nikoleris efi_file_info_t *file_info;
216529ab889SNikos Nikoleris efi_file_io_interface_t *io_if;
217529ab889SNikos Nikoleris efi_file_t *root, *file;
218529ab889SNikos Nikoleris efi_status_t status;
219529ab889SNikos Nikoleris efi_guid_t file_system_proto_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
220529ab889SNikos Nikoleris efi_guid_t file_info_guid = EFI_FILE_INFO_ID;
221529ab889SNikos Nikoleris
222529ab889SNikos Nikoleris /* Open the device */
223529ab889SNikos Nikoleris status = efi_bs_call(handle_protocol, image->device_handle, &file_system_proto_guid,
224529ab889SNikos Nikoleris (void **)&io_if);
225529ab889SNikos Nikoleris if (status != EFI_SUCCESS)
226529ab889SNikos Nikoleris return;
227529ab889SNikos Nikoleris
228529ab889SNikos Nikoleris status = io_if->open_volume(io_if, &root);
229529ab889SNikos Nikoleris if (status != EFI_SUCCESS)
230529ab889SNikos Nikoleris return;
231529ab889SNikos Nikoleris
232529ab889SNikos Nikoleris /* And then open the file */
233529ab889SNikos Nikoleris status = root->open(root, &file, path_name, EFI_FILE_MODE_READ, 0);
234529ab889SNikos Nikoleris if (status != EFI_SUCCESS) {
235529ab889SNikos Nikoleris printf("Failed to open %ls - %lx\n", path_name, status);
236529ab889SNikos Nikoleris assert(status == EFI_SUCCESS);
237529ab889SNikos Nikoleris }
238529ab889SNikos Nikoleris
239529ab889SNikos Nikoleris /* Find the file size in order to allocate the buffer */
240529ab889SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
241529ab889SNikos Nikoleris if (status != EFI_SUCCESS)
242529ab889SNikos Nikoleris return;
243529ab889SNikos Nikoleris
244529ab889SNikos Nikoleris status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
245529ab889SNikos Nikoleris if (status == EFI_BUFFER_TOO_SMALL) {
246529ab889SNikos Nikoleris efi_free_pool(file_info);
247529ab889SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
248529ab889SNikos Nikoleris assert(file_info);
249529ab889SNikos Nikoleris status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
250529ab889SNikos Nikoleris }
251529ab889SNikos Nikoleris assert(status == EFI_SUCCESS);
252529ab889SNikos Nikoleris
253529ab889SNikos Nikoleris buffer_size = file_info->file_size;
254529ab889SNikos Nikoleris
255529ab889SNikos Nikoleris efi_free_pool(file_info);
256529ab889SNikos Nikoleris
257529ab889SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
258529ab889SNikos Nikoleris assert(*data);
259529ab889SNikos Nikoleris /* Perform the actual read */
260529ab889SNikos Nikoleris status = file->read(file, &buffer_size, *data);
261529ab889SNikos Nikoleris if (status == EFI_BUFFER_TOO_SMALL) {
262529ab889SNikos Nikoleris efi_free_pool(*data);
263529ab889SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
264529ab889SNikos Nikoleris status = file->read(file, &buffer_size, *data);
265529ab889SNikos Nikoleris }
266529ab889SNikos Nikoleris assert(status == EFI_SUCCESS);
267529ab889SNikos Nikoleris
268529ab889SNikos Nikoleris *datasize = buffer_size;
269529ab889SNikos Nikoleris }
270529ab889SNikos Nikoleris
efi_grow_buffer(efi_status_t * status,void ** buffer,uint64_t buffer_size)271529ab889SNikos Nikoleris static int efi_grow_buffer(efi_status_t *status, void **buffer, uint64_t buffer_size)
272529ab889SNikos Nikoleris {
273529ab889SNikos Nikoleris int try_again;
274529ab889SNikos Nikoleris
275529ab889SNikos Nikoleris if (!*buffer && buffer_size) {
276529ab889SNikos Nikoleris *status = EFI_BUFFER_TOO_SMALL;
277529ab889SNikos Nikoleris }
278529ab889SNikos Nikoleris
279529ab889SNikos Nikoleris try_again = 0;
280529ab889SNikos Nikoleris if (*status == EFI_BUFFER_TOO_SMALL) {
281529ab889SNikos Nikoleris if (*buffer)
282529ab889SNikos Nikoleris efi_free_pool(*buffer);
283529ab889SNikos Nikoleris
284529ab889SNikos Nikoleris efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, buffer);
285529ab889SNikos Nikoleris if (*buffer) {
286529ab889SNikos Nikoleris try_again = 1;
287529ab889SNikos Nikoleris } else {
288529ab889SNikos Nikoleris *status = EFI_OUT_OF_RESOURCES;
289529ab889SNikos Nikoleris }
290529ab889SNikos Nikoleris }
291529ab889SNikos Nikoleris
292529ab889SNikos Nikoleris if (!try_again && EFI_ERROR(*status) && *buffer) {
293529ab889SNikos Nikoleris efi_free_pool(*buffer);
294529ab889SNikos Nikoleris *buffer = NULL;
295529ab889SNikos Nikoleris }
296529ab889SNikos Nikoleris
297529ab889SNikos Nikoleris return try_again;
298529ab889SNikos Nikoleris }
299529ab889SNikos Nikoleris
efi_get_var(efi_handle_t handle,struct efi_loaded_image_64 * image,efi_char16_t * var)300529ab889SNikos Nikoleris static void* efi_get_var(efi_handle_t handle, struct efi_loaded_image_64 *image, efi_char16_t *var)
301529ab889SNikos Nikoleris {
302529ab889SNikos Nikoleris efi_status_t status = EFI_SUCCESS;
303529ab889SNikos Nikoleris void *val = NULL;
304529ab889SNikos Nikoleris uint64_t val_size = 100;
305529ab889SNikos Nikoleris efi_guid_t efi_var_guid = EFI_VAR_GUID;
306529ab889SNikos Nikoleris
307529ab889SNikos Nikoleris while (efi_grow_buffer(&status, &val, val_size + sizeof(efi_char16_t)))
308529ab889SNikos Nikoleris status = efi_rs_call(get_variable, var, &efi_var_guid, NULL, &val_size, val);
309529ab889SNikos Nikoleris
310529ab889SNikos Nikoleris if (val)
311529ab889SNikos Nikoleris ((efi_char16_t *)val)[val_size / sizeof(efi_char16_t)] = L'\0';
312529ab889SNikos Nikoleris
313529ab889SNikos Nikoleris return val;
314529ab889SNikos Nikoleris }
315529ab889SNikos Nikoleris
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)316529ab889SNikos Nikoleris static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
317529ab889SNikos Nikoleris {
318529ab889SNikos Nikoleris efi_char16_t var[] = ENV_VARNAME_DTBFILE;
319529ab889SNikos Nikoleris efi_char16_t *val;
320529ab889SNikos Nikoleris void *fdt = NULL;
3219632ce44SAndrew Jones int fdtsize = 0;
322529ab889SNikos Nikoleris
323529ab889SNikos Nikoleris val = efi_get_var(handle, image, var);
3249632ce44SAndrew Jones if (val) {
325529ab889SNikos Nikoleris efi_load_image(handle, image, &fdt, &fdtsize, val);
3269632ce44SAndrew Jones if (fdtsize == 0)
3279632ce44SAndrew Jones return NULL;
3289632ce44SAndrew Jones } else if (efi_get_system_config_table(DEVICE_TREE_GUID, &fdt) != EFI_SUCCESS) {
3299632ce44SAndrew Jones return NULL;
3309632ce44SAndrew Jones }
331529ab889SNikos Nikoleris
3329632ce44SAndrew Jones return fdt_check_header(fdt) == 0 ? fdt : NULL;
333529ab889SNikos Nikoleris }
334bb294dfdSPavan Kumar Paluri #else
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)335bb294dfdSPavan Kumar Paluri static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
336bb294dfdSPavan Kumar Paluri {
337bb294dfdSPavan Kumar Paluri return NULL;
338bb294dfdSPavan Kumar Paluri }
339bb294dfdSPavan Kumar Paluri #endif
340529ab889SNikos Nikoleris
341b9f8ff7cSAndrew Jones static const struct {
342b9f8ff7cSAndrew Jones struct efi_vendor_dev_path vendor;
343b9f8ff7cSAndrew Jones struct efi_generic_dev_path end;
344b9f8ff7cSAndrew Jones } __packed initrd_dev_path = {
345b9f8ff7cSAndrew Jones {
346b9f8ff7cSAndrew Jones {
347b9f8ff7cSAndrew Jones EFI_DEV_MEDIA,
348b9f8ff7cSAndrew Jones EFI_DEV_MEDIA_VENDOR,
349b9f8ff7cSAndrew Jones sizeof(struct efi_vendor_dev_path),
350b9f8ff7cSAndrew Jones },
351b9f8ff7cSAndrew Jones LINUX_EFI_INITRD_MEDIA_GUID
352b9f8ff7cSAndrew Jones }, {
353b9f8ff7cSAndrew Jones EFI_DEV_END_PATH,
354b9f8ff7cSAndrew Jones EFI_DEV_END_ENTIRE,
355b9f8ff7cSAndrew Jones sizeof(struct efi_generic_dev_path)
356b9f8ff7cSAndrew Jones }
357b9f8ff7cSAndrew Jones };
358b9f8ff7cSAndrew Jones
efi_load_initrd(void)359b9f8ff7cSAndrew Jones static void efi_load_initrd(void)
360b9f8ff7cSAndrew Jones {
361b9f8ff7cSAndrew Jones efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
362b9f8ff7cSAndrew Jones efi_device_path_protocol_t *dp;
363b9f8ff7cSAndrew Jones efi_load_file2_protocol_t *lf2;
364b9f8ff7cSAndrew Jones efi_handle_t handle;
365b9f8ff7cSAndrew Jones efi_status_t status;
366b9f8ff7cSAndrew Jones unsigned long file_size = 0;
367b9f8ff7cSAndrew Jones
368b9f8ff7cSAndrew Jones initrd = NULL;
369b9f8ff7cSAndrew Jones initrd_size = 0;
370b9f8ff7cSAndrew Jones
371b9f8ff7cSAndrew Jones dp = (efi_device_path_protocol_t *)&initrd_dev_path;
372b9f8ff7cSAndrew Jones status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
373b9f8ff7cSAndrew Jones if (status != EFI_SUCCESS)
374b9f8ff7cSAndrew Jones return;
375b9f8ff7cSAndrew Jones
376b9f8ff7cSAndrew Jones status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, (void **)&lf2);
377b9f8ff7cSAndrew Jones assert(status == EFI_SUCCESS);
378b9f8ff7cSAndrew Jones
379b9f8ff7cSAndrew Jones status = efi_call_proto(lf2, load_file, dp, false, &file_size, NULL);
380b9f8ff7cSAndrew Jones assert(status == EFI_BUFFER_TOO_SMALL);
381b9f8ff7cSAndrew Jones
382b9f8ff7cSAndrew Jones status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, file_size, (void **)&initrd);
383b9f8ff7cSAndrew Jones assert(status == EFI_SUCCESS);
384b9f8ff7cSAndrew Jones
385b9f8ff7cSAndrew Jones status = efi_call_proto(lf2, load_file, dp, false, &file_size, (void *)initrd);
386b9f8ff7cSAndrew Jones assert(status == EFI_SUCCESS);
387b9f8ff7cSAndrew Jones
388b9f8ff7cSAndrew Jones initrd_size = (u32)file_size;
389b9f8ff7cSAndrew Jones
390b9f8ff7cSAndrew Jones /*
391b9f8ff7cSAndrew Jones * UEFI appends initrd=initrd to the command line when an initrd is present.
392b9f8ff7cSAndrew Jones * Remove it in order to avoid confusing unit tests.
393b9f8ff7cSAndrew Jones */
394b9f8ff7cSAndrew Jones if (!strcmp(__argv[__argc - 1], "initrd=initrd")) {
395b9f8ff7cSAndrew Jones __argv[__argc - 1] = NULL;
396b9f8ff7cSAndrew Jones __argc -= 1;
397b9f8ff7cSAndrew Jones }
398b9f8ff7cSAndrew Jones }
399b9f8ff7cSAndrew Jones
efi_main(efi_handle_t handle,efi_system_table_t * sys_tab)400ad5fb883SZixuan Wang efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
4010eaa5176SVarad Gautam {
402ad5fb883SZixuan Wang int ret;
4031ae9072eSZixuan Wang efi_status_t status;
4041ae9072eSZixuan Wang efi_bootinfo_t efi_bootinfo;
405ad5fb883SZixuan Wang
4060eaa5176SVarad Gautam efi_system_table = sys_tab;
4070eaa5176SVarad Gautam
408b4e8c300SZixuan Wang /* Memory map struct values */
409b365a5b0SPavan Kumar Paluri efi_memory_desc_t *map;
410b365a5b0SPavan Kumar Paluri unsigned long map_size, desc_size, key, buff_size;
411b4e8c300SZixuan Wang u32 desc_ver;
412b4e8c300SZixuan Wang
41385c3c524SNikos Nikoleris /* Helper variables needed to get the cmdline */
41485c3c524SNikos Nikoleris struct efi_loaded_image_64 *image;
41585c3c524SNikos Nikoleris efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
41685c3c524SNikos Nikoleris char *cmdline_ptr = NULL;
41785c3c524SNikos Nikoleris int cmdline_size = 0;
41885c3c524SNikos Nikoleris
41985c3c524SNikos Nikoleris /*
42085c3c524SNikos Nikoleris * Get a handle to the loaded image protocol. This is used to get
42185c3c524SNikos Nikoleris * information about the running image, such as size and the command
42285c3c524SNikos Nikoleris * line.
42385c3c524SNikos Nikoleris */
42485c3c524SNikos Nikoleris status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, (void *)&image);
42585c3c524SNikos Nikoleris if (status != EFI_SUCCESS) {
42685c3c524SNikos Nikoleris printf("Failed to get loaded image protocol\n");
42785c3c524SNikos Nikoleris goto efi_main_error;
42885c3c524SNikos Nikoleris }
42985c3c524SNikos Nikoleris
43085c3c524SNikos Nikoleris cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
43185c3c524SNikos Nikoleris if (!cmdline_ptr) {
43285c3c524SNikos Nikoleris printf("getting command line via LOADED_IMAGE_PROTOCOL\n");
43385c3c524SNikos Nikoleris status = EFI_OUT_OF_RESOURCES;
43485c3c524SNikos Nikoleris goto efi_main_error;
43585c3c524SNikos Nikoleris }
43685c3c524SNikos Nikoleris setup_args(cmdline_ptr);
43785c3c524SNikos Nikoleris
438b9f8ff7cSAndrew Jones efi_load_initrd();
439b9f8ff7cSAndrew Jones
440529ab889SNikos Nikoleris efi_bootinfo.fdt = efi_get_fdt(handle, image);
441b4e8c300SZixuan Wang /* Set up efi_bootinfo */
442b4e8c300SZixuan Wang efi_bootinfo.mem_map.map = ↦
443b4e8c300SZixuan Wang efi_bootinfo.mem_map.map_size = &map_size;
444b4e8c300SZixuan Wang efi_bootinfo.mem_map.desc_size = &desc_size;
445b4e8c300SZixuan Wang efi_bootinfo.mem_map.desc_ver = &desc_ver;
446b4e8c300SZixuan Wang efi_bootinfo.mem_map.key_ptr = &key;
447b4e8c300SZixuan Wang efi_bootinfo.mem_map.buff_size = &buff_size;
448b4e8c300SZixuan Wang
449ba0a6fd4SAndrew Jones #ifdef __riscv
450ba0a6fd4SAndrew Jones status = efi_get_boot_hartid();
451ba0a6fd4SAndrew Jones if (status != EFI_SUCCESS) {
452ba0a6fd4SAndrew Jones printf("Failed to get boot haritd\n");
453ba0a6fd4SAndrew Jones goto efi_main_error;
454ba0a6fd4SAndrew Jones }
455ba0a6fd4SAndrew Jones #endif
456ba0a6fd4SAndrew Jones
457b365a5b0SPavan Kumar Paluri status = EFI_INVALID_PARAMETER;
458b365a5b0SPavan Kumar Paluri while (status == EFI_INVALID_PARAMETER) {
459b365a5b0SPavan Kumar Paluri status = efi_get_memory_map(&efi_bootinfo.mem_map);
460b365a5b0SPavan Kumar Paluri if (status != EFI_SUCCESS) {
461b365a5b0SPavan Kumar Paluri printf("Failed to get memory map\n");
462b365a5b0SPavan Kumar Paluri goto efi_main_error;
463b365a5b0SPavan Kumar Paluri }
464b4e8c300SZixuan Wang /*
465b365a5b0SPavan Kumar Paluri * Exit EFI boot services, let kvm-unit-tests take full
466b365a5b0SPavan Kumar Paluri * control of the guest.
467b4e8c300SZixuan Wang */
468b4e8c300SZixuan Wang status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
469b365a5b0SPavan Kumar Paluri if (status == EFI_INVALID_PARAMETER)
470b365a5b0SPavan Kumar Paluri efi_free_pool(*efi_bootinfo.mem_map.map);
471b365a5b0SPavan Kumar Paluri }
472b365a5b0SPavan Kumar Paluri
4731ae9072eSZixuan Wang if (status != EFI_SUCCESS) {
4741ae9072eSZixuan Wang printf("Failed to exit boot services\n");
475b4e8c300SZixuan Wang goto efi_main_error;
4761ae9072eSZixuan Wang }
4771ae9072eSZixuan Wang
478b4e8c300SZixuan Wang /* Set up arch-specific resources */
479b4e8c300SZixuan Wang status = setup_efi(&efi_bootinfo);
480b4e8c300SZixuan Wang if (status != EFI_SUCCESS) {
481b4e8c300SZixuan Wang printf("Failed to set up arch-specific resources\n");
482b4e8c300SZixuan Wang goto efi_main_error;
483b4e8c300SZixuan Wang }
484b4e8c300SZixuan Wang
48525ef5154SNadav Amit printf("Address of image is: 0x%lx\n", (unsigned long)&_text);
48625ef5154SNadav Amit
487b4e8c300SZixuan Wang /* Run the test case */
488ad5fb883SZixuan Wang ret = main(__argc, __argv, __environ);
489ad5fb883SZixuan Wang
4902f47d025SZixuan Wang /* Shutdown the guest VM */
4912f47d025SZixuan Wang efi_exit(ret);
492ad5fb883SZixuan Wang
493ad5fb883SZixuan Wang /* Unreachable */
494ad5fb883SZixuan Wang return EFI_UNSUPPORTED;
495b4e8c300SZixuan Wang
496b4e8c300SZixuan Wang efi_main_error:
497b4e8c300SZixuan Wang /* Shutdown the guest with error EFI status */
4982f47d025SZixuan Wang efi_exit(status);
499b4e8c300SZixuan Wang
500b4e8c300SZixuan Wang /* Unreachable */
501b4e8c300SZixuan Wang return EFI_UNSUPPORTED;
5020eaa5176SVarad Gautam }
503