diff options
Diffstat (limited to 'sys/boot/fdt/fdt_loader_cmd.c')
-rw-r--r-- | sys/boot/fdt/fdt_loader_cmd.c | 125 |
1 files changed, 107 insertions, 18 deletions
diff --git a/sys/boot/fdt/fdt_loader_cmd.c b/sys/boot/fdt/fdt_loader_cmd.c index 46c6d9a..144aa3b 100644 --- a/sys/boot/fdt/fdt_loader_cmd.c +++ b/sys/boot/fdt/fdt_loader_cmd.c @@ -33,6 +33,9 @@ __FBSDID("$FreeBSD$"); #include <stand.h> #include <fdt.h> #include <libfdt.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <machine/elf.h> #include "bootstrap.h" #include "glue.h" @@ -56,6 +59,10 @@ __FBSDID("$FreeBSD$"); #define MIN(num1, num2) (((num1) < (num2)) ? (num1):(num2)) +#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) + +#define FDT_STATIC_DTB_SYMBOL "fdt_static_dtb" + static struct fdt_header *fdtp = NULL; static int fdt_cmd_nyi(int argc, char *argv[]); @@ -92,6 +99,86 @@ static const struct cmdtab commands[] = { static char cwd[FDT_CWD_LEN] = "/"; +static vm_offset_t +fdt_find_static_dtb(void) +{ + Elf_Sym sym; + vm_offset_t dyntab, esym; + uint64_t offs; + struct preloaded_file *kfp; + struct file_metadata *md; + Elf_Sym *symtab; + Elf_Dyn *dyn; + char *strtab, *strp; + int i; + + esym = strtab = symtab = 0; + + offs = __elfN(relocation_offset); + + kfp = file_findfile(NULL, NULL); + if (kfp == NULL) + return (0); + + md = file_findmetadata(kfp, MODINFOMD_ESYM); + if (md == NULL) + return (0); + COPYOUT(md->md_data, &esym, sizeof(esym)); + + md = file_findmetadata(kfp, MODINFOMD_DYNAMIC); + if (md == NULL) + return (0); + COPYOUT(md->md_data, &dyntab, sizeof(dyntab)); + dyntab += offs; + + /* Locate STRTAB and DYNTAB */ + for (dyn = (Elf_Dyn *)dyntab; dyn->d_tag != DT_NULL; dyn++) { + if (dyn->d_tag == DT_STRTAB) { + strtab = (char *)(uintptr_t)(dyn->d_un.d_ptr + offs); + continue; + } else if (dyn->d_tag == DT_SYMTAB) { + symtab = (Elf_Sym *)(uintptr_t) + (dyn->d_un.d_ptr + offs); + continue; + } + } + + if (symtab == NULL || strtab == NULL) { + /* + * No symtab? No strtab? That should not happen here, + * and should have been verified during __elfN(loadimage). + * This must be some kind of a bug. + */ + return (0); + } + + /* + * The most efficent way to find a symbol would be to calculate a + * hash, find proper bucket and chain, and thus find a symbol. + * However, that would involve code duplication (e.g. for hash + * function). So we're using simpler and a bit slower way: we're + * iterating through symbols, searching for the one which name is + * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit, + * we are eliminating symbols type of which is not STT_NOTYPE, or(and) + * those which binding attribute is not STB_GLOBAL. + */ + for (i = 0; (vm_offset_t)(symtab + i) < esym; i++) { + COPYOUT(symtab + i, &sym, sizeof(sym)); + if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL || + ELF_ST_TYPE(sym.st_info) != STT_NOTYPE) + continue; + + strp = strdupout((vm_offset_t)(strtab + sym.st_name)); + if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0) { + /* Found a match ! */ + free(strp); + return ((vm_offset_t)(sym.st_value + offs)); + } + free(strp); + } + return (0); +} + static int fdt_setup_fdtp() { @@ -103,10 +190,14 @@ fdt_setup_fdtp() */ bfp = file_findfile(NULL, "dtb"); if (bfp == NULL) { - command_errmsg = "no device tree blob loaded"; - return (CMD_ERROR); + if ((fdtp = (struct fdt_head *)fdt_find_static_dtb()) == 0) { + command_errmsg = "no device tree blob found!"; + return (CMD_ERROR); + } + } else { + /* Dynamic blob has precedence over static. */ + fdtp = (struct fdt_header *)bfp->f_addr; } - fdtp = (struct fdt_header *)bfp->f_addr; /* * Validate the blob. @@ -448,7 +539,10 @@ fixup_stdout(const char *env) } } -int +/* + * Locate the blob, fix it up and return its location. + */ +void * fdt_fixup(void) { const char *env; @@ -461,13 +555,10 @@ fdt_fixup(void) ethstr = NULL; len = 0; - if (!fdtp) { - err = fdt_setup_fdtp(); - if (err) { - sprintf(command_errbuf, "Could not perform blob " - "fixups. Error code: %d\n", err); - return (err); - } + err = fdt_setup_fdtp(); + if (err) { + sprintf(command_errbuf, "No valid device tree blob found!"); + return (NULL); } /* Create /chosen node (if not exists) */ @@ -477,7 +568,7 @@ fdt_fixup(void) /* Value assigned to fixup-applied does not matter. */ if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL)) - return (CMD_OK); + goto success; /* Acquire sys_info */ si = ub_get_sys_info(); @@ -521,7 +612,8 @@ fdt_fixup(void) fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0); - return (CMD_OK); +success: + return (fdtp); } int @@ -539,7 +631,8 @@ command_fdt_internal(int argc, char *argv[]) /* * Check if uboot env vars were parsed already. If not, do it now. */ - fdt_fixup(); + if (fdt_fixup() == NULL) + return (CMD_ERROR); /* * Validate fdt <command>. @@ -560,10 +653,6 @@ command_fdt_internal(int argc, char *argv[]) return (CMD_ERROR); } - if (!fdtp) - if (fdt_setup_fdtp()) - return (CMD_ERROR); - /* * Call command handler. */ |