Search code examples
cu-boot

How ll_entry_declare() works in U-Boot?


When the U-boot shell wants to find a command from the linker_list, it invokes the following function -

cmd_tbl_t *find_cmd(const char *cmd)
{
         cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
         const int len = ll_entry_count(cmd_tbl_t, cmd);
         return find_cmd_tbl(cmd, start, len);
}

To get a pointer to the struct cmd_tbl_t which holds all the specifics of this command, ll_entry_start() is invoked.

#define ll_entry_start(_type, _list)                                    
 ({                                                                      
         static char start[0] __aligned(4) __attribute__((unused,        
                 section(".u_boot_list_2_"#_list"_1")));                 
         (_type *)&start;                                                
 })

ll_entry_start gives back pointer to the start of the struct cmd_tbl_t holding the command.

We cannot predict what is the string passed to find_cmd().

The #(stringizing) operator is a pre-processor directive. It cannot generate a string from a token name in runtime. How does the compiler generate code for all the possible string values that _list can take in this?


Solution

  • The linker generated list for the commands is filled by specifying the commands with the U_BOOT_CMD macro. Here is an example from cmd/bootefi.c

    U_BOOT_CMD(
            bootefi, 3, 0, do_bootefi,
            "Boots an EFI payload from memory",
            bootefi_help_text
    );
    

    The linker creates a section in the u-boot binary for each command, e.g. for bootefi: .u_boot_list_2_cmd_2_bootefi

    You can iterate over linker generated lists using ll_entry_start and ll_entry_next. Below is an example taken from lib/efi_selftest/efi_selftest.c:

    for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
         test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
            efi_st_printf("'%s'%s\n", test->name,
                          test->on_request ? " - on request" : "");
    }