Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Mar 2012 13:08:57 +0000 (UTC)
From:      Rafal Jaworowski <raj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r233230 - in head/sys/boot: fdt uboot/common
Message-ID:  <201203201308.q2KD8vFb021315@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: raj
Date: Tue Mar 20 13:08:57 2012
New Revision: 233230
URL: http://svn.freebsd.org/changeset/base/233230

Log:
  Improve device tree blob (DTB) handling in loader(8).
  
  Enable using the statically embedded blob from the kernel, if present. The KLD
  loaded DTB takes precedence, but they are both recognized and handled in the
  same way.
  
  Submitted by:	Lukasz Wojcik
  Obtained from:	Semihalf
  MFC after:	1 week

Modified:
  head/sys/boot/fdt/fdt_loader_cmd.c
  head/sys/boot/uboot/common/metadata.c

Modified: head/sys/boot/fdt/fdt_loader_cmd.c
==============================================================================
--- head/sys/boot/fdt/fdt_loader_cmd.c	Tue Mar 20 12:24:36 2012	(r233229)
+++ head/sys/boot/fdt/fdt_loader_cmd.c	Tue Mar 20 13:08:57 2012	(r233230)
@@ -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 *arg
 	/*
 	 * 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 *arg
 		return (CMD_ERROR);
 	}
 
-	if (!fdtp)
-		if (fdt_setup_fdtp())
-			return (CMD_ERROR);
-
 	/*
 	 * Call command handler.
 	 */

Modified: head/sys/boot/uboot/common/metadata.c
==============================================================================
--- head/sys/boot/uboot/common/metadata.c	Tue Mar 20 12:24:36 2012	(r233229)
+++ head/sys/boot/uboot/common/metadata.c	Tue Mar 20 13:08:57 2012	(r233230)
@@ -333,13 +333,12 @@ md_load(char *args, vm_offset_t *modulep
 
 #if defined(LOADER_FDT_SUPPORT)
 	/* Handle device tree blob */
-	fdt_fixup();
-	if ((bfp = file_findfile(NULL, "dtb")) == NULL &&
-	    (howto & RB_VERBOSE))
-		printf("**WARNING** Booting with no DTB loaded!\n");
-
-	dtbp = bfp == NULL ? 0 : bfp->f_addr;
-	file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+	dtbp = fdt_fixup();
+	if (dtbp != (vm_offset_t)NULL)
+		file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp);
+	else
+		pager_output("WARNING! Trying to fire up the kernel, but no "
+		    "device tree blob found!\n");
 #endif
 
 	file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203201308.q2KD8vFb021315>