2010-05-25 15:21:39 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2009-2010 The FreeBSD Foundation
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This software was developed by Semihalf under sponsorship from
|
|
|
|
* the FreeBSD Foundation.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include <stand.h>
|
|
|
|
#include <libfdt.h>
|
2017-12-30 06:53:27 +00:00
|
|
|
#include <fdt.h>
|
2012-03-20 13:08:57 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/linker.h>
|
|
|
|
#include <machine/elf.h>
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
#include "bootstrap.h"
|
2014-11-01 10:50:18 +00:00
|
|
|
#include "fdt_platform.h"
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
|
|
|
|
printf(fmt,##args); } while (0)
|
|
|
|
#else
|
|
|
|
#define debugf(fmt, args...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define FDT_CWD_LEN 256
|
2016-07-01 21:09:30 +00:00
|
|
|
#define FDT_MAX_DEPTH 12
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
#define FDT_PROP_SEP " = "
|
|
|
|
|
2012-05-17 04:04:48 +00:00
|
|
|
#define COPYOUT(s,d,l) archsw.arch_copyout(s, d, l)
|
|
|
|
#define COPYIN(s,d,l) archsw.arch_copyin(s, d, l)
|
2012-03-20 13:08:57 +00:00
|
|
|
|
|
|
|
#define FDT_STATIC_DTB_SYMBOL "fdt_static_dtb"
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
#define CMD_REQUIRES_BLOB 0x01
|
|
|
|
|
2013-02-23 20:34:47 +00:00
|
|
|
/* Location of FDT yet to be loaded. */
|
2013-02-25 01:50:04 +00:00
|
|
|
/* This may be in read-only memory, so can't be manipulated directly. */
|
2013-02-23 20:34:47 +00:00
|
|
|
static struct fdt_header *fdt_to_load = NULL;
|
2013-02-25 01:50:04 +00:00
|
|
|
/* Location of FDT on heap. */
|
|
|
|
/* This is the copy we actually manipulate. */
|
2010-05-25 15:21:39 +00:00
|
|
|
static struct fdt_header *fdtp = NULL;
|
2012-05-17 04:04:48 +00:00
|
|
|
/* Size of FDT blob */
|
|
|
|
static size_t fdtp_size = 0;
|
2019-05-23 19:26:50 +00:00
|
|
|
/* Have we loaded all the needed overlays */
|
|
|
|
static int fdt_overlays_applied = 0;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
static int fdt_load_dtb(vm_offset_t va);
|
2018-01-23 18:03:13 +00:00
|
|
|
static void fdt_print_overlay_load_error(int err, const char *filename);
|
2018-01-28 03:07:22 +00:00
|
|
|
static int fdt_check_overlay_compatible(void *base_fdt, void *overlay_fdt);
|
2012-11-30 03:15:50 +00:00
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
static int fdt_cmd_nyi(int argc, char *argv[]);
|
stand/fdt: Consolidate overlay handling a little further
This should have been done as part of r327350, but due to lack of foresight
it came later. In the different places we apply overlays, we duplicate the
bits that check for fdt_overlays in the environment and supplement that with
any other places we need to check for overlays to load. These "other places"
will be loader specific and are not candidates for consolidation.
Provide an fdt_load_dtb_overlays to capture the common logic, allow passing
in an additional list of overlays to be loaded. This additional list of
overlays is used in practice for ubldr to pull in any fdt_overlays passed to
it from U-Boot environment, but it can be used for any other source of
overlays.
These additional overlays supplement loader.conf(5) fdt_overlays, rather
than replace, so that we're not restricted to specifying overlays in only
one place. This is a change from previous behavior where loader.conf(5)
supplied fdt_overlays would cause us to ignore U-Boot environment, and this
seems nonsensical- user should have sufficient control over both of these
aspects, or lack of control for good reasons.
A knob could be considered in the future to ignore U-Boot supplied overlays,
but the supplemental treatment seems like a good start.
Reviewed by: imp (earlier version), gonzo (earlier version)
Differential Revision: https://reviews.freebsd.org/D13993
2018-01-28 01:22:15 +00:00
|
|
|
static int fdt_load_dtb_overlays_string(const char * filenames);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
static int fdt_cmd_addr(int argc, char *argv[]);
|
2010-05-25 15:21:39 +00:00
|
|
|
static int fdt_cmd_mkprop(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_cd(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_hdr(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_ls(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_prop(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_pwd(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_rm(int argc, char *argv[]);
|
|
|
|
static int fdt_cmd_mknode(int argc, char *argv[]);
|
2012-11-30 03:15:50 +00:00
|
|
|
static int fdt_cmd_mres(int argc, char *argv[]);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
typedef int cmdf_t(int, char *[]);
|
|
|
|
|
|
|
|
struct cmdtab {
|
2014-10-31 18:35:03 +00:00
|
|
|
const char *name;
|
|
|
|
cmdf_t *handler;
|
|
|
|
int flags;
|
2010-05-25 15:21:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct cmdtab commands[] = {
|
2012-11-30 03:15:50 +00:00
|
|
|
{ "addr", &fdt_cmd_addr, 0 },
|
|
|
|
{ "alias", &fdt_cmd_nyi, 0 },
|
|
|
|
{ "cd", &fdt_cmd_cd, CMD_REQUIRES_BLOB },
|
|
|
|
{ "header", &fdt_cmd_hdr, CMD_REQUIRES_BLOB },
|
|
|
|
{ "ls", &fdt_cmd_ls, CMD_REQUIRES_BLOB },
|
|
|
|
{ "mknode", &fdt_cmd_mknode, CMD_REQUIRES_BLOB },
|
|
|
|
{ "mkprop", &fdt_cmd_mkprop, CMD_REQUIRES_BLOB },
|
|
|
|
{ "mres", &fdt_cmd_mres, CMD_REQUIRES_BLOB },
|
|
|
|
{ "prop", &fdt_cmd_prop, CMD_REQUIRES_BLOB },
|
|
|
|
{ "pwd", &fdt_cmd_pwd, CMD_REQUIRES_BLOB },
|
|
|
|
{ "rm", &fdt_cmd_rm, CMD_REQUIRES_BLOB },
|
2010-05-25 15:21:39 +00:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static char cwd[FDT_CWD_LEN] = "/";
|
|
|
|
|
2012-03-20 13:08:57 +00:00
|
|
|
static vm_offset_t
|
2012-05-17 04:04:48 +00:00
|
|
|
fdt_find_static_dtb()
|
2012-03-20 13:08:57 +00:00
|
|
|
{
|
2013-03-10 00:43:01 +00:00
|
|
|
Elf_Ehdr *ehdr;
|
|
|
|
Elf_Shdr *shdr;
|
2012-03-20 13:08:57 +00:00
|
|
|
Elf_Sym sym;
|
2013-03-10 00:43:01 +00:00
|
|
|
vm_offset_t strtab, symtab, fdt_start;
|
2012-03-20 13:08:57 +00:00
|
|
|
uint64_t offs;
|
|
|
|
struct preloaded_file *kfp;
|
|
|
|
struct file_metadata *md;
|
2012-05-17 04:04:48 +00:00
|
|
|
char *strp;
|
2013-03-10 00:43:01 +00:00
|
|
|
int i, sym_count;
|
2012-03-20 13:08:57 +00:00
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
debugf("fdt_find_static_dtb()\n");
|
|
|
|
|
2013-03-30 16:33:16 +00:00
|
|
|
sym_count = symtab = strtab = 0;
|
2012-05-17 04:04:48 +00:00
|
|
|
strp = NULL;
|
2012-03-20 13:08:57 +00:00
|
|
|
|
|
|
|
offs = __elfN(relocation_offset);
|
|
|
|
|
|
|
|
kfp = file_findfile(NULL, NULL);
|
|
|
|
if (kfp == NULL)
|
|
|
|
return (0);
|
|
|
|
|
2013-03-10 00:43:01 +00:00
|
|
|
/* Locate the dynamic symbols and strtab. */
|
|
|
|
md = file_findmetadata(kfp, MODINFOMD_ELFHDR);
|
2012-03-20 13:08:57 +00:00
|
|
|
if (md == NULL)
|
|
|
|
return (0);
|
2013-03-10 00:43:01 +00:00
|
|
|
ehdr = (Elf_Ehdr *)md->md_data;
|
2012-03-20 13:08:57 +00:00
|
|
|
|
2013-03-10 00:43:01 +00:00
|
|
|
md = file_findmetadata(kfp, MODINFOMD_SHDR);
|
2012-03-20 13:08:57 +00:00
|
|
|
if (md == NULL)
|
|
|
|
return (0);
|
2013-03-10 00:43:01 +00:00
|
|
|
shdr = (Elf_Shdr *)md->md_data;
|
|
|
|
|
|
|
|
for (i = 0; i < ehdr->e_shnum; ++i) {
|
|
|
|
if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) {
|
|
|
|
symtab = shdr[i].sh_addr + offs;
|
|
|
|
sym_count = shdr[i].sh_size / sizeof(Elf_Sym);
|
|
|
|
} else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) {
|
|
|
|
strtab = shdr[i].sh_addr + offs;
|
2012-03-20 13:08:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-04-30 00:26:38 +00:00
|
|
|
* The most efficient way to find a symbol would be to calculate a
|
2012-03-20 13:08:57 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
2012-05-17 04:04:48 +00:00
|
|
|
fdt_start = 0;
|
|
|
|
while (sym_count > 0 && fdt_start == 0) {
|
|
|
|
COPYOUT(symtab, &sym, sizeof(sym));
|
|
|
|
symtab += sizeof(sym);
|
|
|
|
--sym_count;
|
2012-03-20 13:08:57 +00:00
|
|
|
if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
|
|
|
|
ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
|
|
|
|
continue;
|
2012-05-17 04:04:48 +00:00
|
|
|
strp = strdupout(strtab + sym.st_name);
|
|
|
|
if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0)
|
|
|
|
fdt_start = (vm_offset_t)sym.st_value + offs;
|
2012-03-20 13:08:57 +00:00
|
|
|
free(strp);
|
|
|
|
}
|
2012-05-17 04:04:48 +00:00
|
|
|
return (fdt_start);
|
2012-03-20 13:08:57 +00:00
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
static int
|
2012-11-30 03:15:50 +00:00
|
|
|
fdt_load_dtb(vm_offset_t va)
|
2010-05-25 15:21:39 +00:00
|
|
|
{
|
2012-05-17 04:04:48 +00:00
|
|
|
struct fdt_header header;
|
2010-05-25 15:21:39 +00:00
|
|
|
int err;
|
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va);
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
COPYOUT(va, &header, sizeof(header));
|
|
|
|
err = fdt_check_header(&header);
|
|
|
|
if (err < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
if (err == -FDT_ERR_BADVERSION) {
|
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
2012-11-30 03:15:50 +00:00
|
|
|
"incompatible blob version: %d, should be: %d",
|
|
|
|
fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION);
|
2016-08-20 16:23:19 +00:00
|
|
|
} else {
|
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"error validating blob: %s", fdt_strerror(err));
|
|
|
|
}
|
2012-11-30 03:15:50 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
/*
|
2012-11-30 03:15:50 +00:00
|
|
|
* Release previous blob
|
2010-05-25 15:21:39 +00:00
|
|
|
*/
|
2012-11-30 03:15:50 +00:00
|
|
|
if (fdtp)
|
|
|
|
free(fdtp);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2012-05-17 04:04:48 +00:00
|
|
|
fdtp_size = fdt_totalsize(&header);
|
|
|
|
fdtp = malloc(fdtp_size);
|
2012-11-30 03:15:50 +00:00
|
|
|
|
2012-05-17 04:04:48 +00:00
|
|
|
if (fdtp == NULL) {
|
|
|
|
command_errmsg = "can't allocate memory for device tree copy";
|
2012-11-30 03:15:50 +00:00
|
|
|
return (1);
|
2012-05-17 04:04:48 +00:00
|
|
|
}
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
COPYOUT(va, fdtp, fdtp_size);
|
|
|
|
debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
int
|
2013-02-20 16:32:38 +00:00
|
|
|
fdt_load_dtb_addr(struct fdt_header *header)
|
2012-11-30 03:15:50 +00:00
|
|
|
{
|
2014-02-13 04:13:50 +00:00
|
|
|
int err;
|
2012-11-30 03:15:50 +00:00
|
|
|
|
2014-05-21 18:02:19 +00:00
|
|
|
debugf("fdt_load_dtb_addr(%p)\n", header);
|
2014-02-22 19:10:58 +00:00
|
|
|
|
2013-02-25 01:50:04 +00:00
|
|
|
fdtp_size = fdt_totalsize(header);
|
2014-02-20 13:09:08 +00:00
|
|
|
err = fdt_check_header(header);
|
2014-02-13 04:13:50 +00:00
|
|
|
if (err < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"error validating blob: %s", fdt_strerror(err));
|
2014-02-13 04:13:50 +00:00
|
|
|
return (err);
|
|
|
|
}
|
2013-02-25 01:50:04 +00:00
|
|
|
free(fdtp);
|
|
|
|
if ((fdtp = malloc(fdtp_size)) == NULL) {
|
|
|
|
command_errmsg = "can't allocate memory for device tree copy";
|
2013-02-20 16:32:38 +00:00
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
2013-02-25 01:50:04 +00:00
|
|
|
|
|
|
|
bcopy(header, fdtp, fdtp_size);
|
|
|
|
return (0);
|
2013-02-20 16:32:38 +00:00
|
|
|
}
|
2012-11-30 03:15:50 +00:00
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
int
|
2014-02-22 22:18:20 +00:00
|
|
|
fdt_load_dtb_file(const char * filename)
|
|
|
|
{
|
|
|
|
struct preloaded_file *bfp, *oldbfp;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
debugf("fdt_load_dtb_file(%s)\n", filename);
|
|
|
|
|
|
|
|
oldbfp = file_findfile(NULL, "dtb");
|
|
|
|
|
|
|
|
/* Attempt to load and validate a new dtb from a file. */
|
loader: implement multiboot support for Xen Dom0
Implement a subset of the multiboot specification in order to boot Xen
and a FreeBSD Dom0 from the FreeBSD bootloader. This multiboot
implementation is tailored to boot Xen and FreeBSD Dom0, and it will
most surely fail to boot any other multiboot compilant kernel.
In order to detect and boot the Xen microkernel, two new file formats
are added to the bootloader, multiboot and multiboot_obj. Multiboot
support must be tested before regular ELF support, since Xen is a
multiboot kernel that also uses ELF. After a multiboot kernel is
detected, all the other loaded kernels/modules are parsed by the
multiboot_obj format.
The layout of the loaded objects in memory is the following; first the
Xen kernel is loaded as a 32bit ELF into memory (Xen will switch to
long mode by itself), after that the FreeBSD kernel is loaded as a RAW
file (Xen will parse and load it using it's internal ELF loader), and
finally the metadata and the modules are loaded using the native
FreeBSD way. After everything is loaded we jump into Xen's entry point
using a small trampoline. The order of the multiboot modules passed to
Xen is the following, the first module is the RAW FreeBSD kernel, and
the second module is the metadata and the FreeBSD modules.
Since Xen will relocate the memory position of the second
multiboot module (the one that contains the metadata and native
FreeBSD modules), we need to stash the original modulep address inside
of the metadata itself in order to recalculate its position once
booted. This also means the metadata must come before the loaded
modules, so after loading the FreeBSD kernel a portion of memory is
reserved in order to place the metadata before booting.
In order to tell the loader to boot Xen and then the FreeBSD kernel the
following has to be added to the /boot/loader.conf file:
xen_cmdline="dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga"
xen_kernel="/boot/xen"
The first argument contains the command line that will be passed to the Xen
kernel, while the second argument is the path to the Xen kernel itself. This
can also be done manually from the loader command line, by for example
typing the following set of commands:
OK unload
OK load /boot/xen dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga
OK load kernel
OK load zfs
OK load if_tap
OK load ...
OK boot
Sponsored by: Citrix Systems R&D
Reviewed by: jhb
Differential Revision: https://reviews.freebsd.org/D517
For the Forth bits:
Submitted by: Julien Grall <julien.grall AT citrix.com>
2015-01-15 16:27:20 +00:00
|
|
|
if ((bfp = file_loadraw(filename, "dtb", 1)) == NULL) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"failed to load file '%s'", filename);
|
2014-02-22 22:18:20 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if ((err = fdt_load_dtb(bfp->f_addr)) != 0) {
|
|
|
|
file_discard(bfp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A new dtb was validated, discard any previous file. */
|
|
|
|
if (oldbfp)
|
|
|
|
file_discard(oldbfp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2016-04-29 22:42:59 +00:00
|
|
|
static int
|
|
|
|
fdt_load_dtb_overlay(const char * filename)
|
|
|
|
{
|
2017-12-28 21:09:36 +00:00
|
|
|
struct preloaded_file *bfp;
|
2016-04-29 22:42:59 +00:00
|
|
|
struct fdt_header header;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
debugf("fdt_load_dtb_overlay(%s)\n", filename);
|
|
|
|
|
2018-01-23 18:03:13 +00:00
|
|
|
/* Attempt to load and validate a new dtb from a file. FDT_ERR_NOTFOUND
|
|
|
|
* is normally a libfdt error code, but libfdt would actually return
|
|
|
|
* -FDT_ERR_NOTFOUND. We re-purpose the error code here to convey a
|
|
|
|
* similar meaning: the file itself was not found, which can still be
|
|
|
|
* considered an error dealing with FDT pieces.
|
|
|
|
*/
|
|
|
|
if ((bfp = file_loadraw(filename, "dtbo", 1)) == NULL)
|
|
|
|
return (FDT_ERR_NOTFOUND);
|
2016-04-29 22:42:59 +00:00
|
|
|
|
|
|
|
COPYOUT(bfp->f_addr, &header, sizeof(header));
|
|
|
|
err = fdt_check_header(&header);
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
file_discard(bfp);
|
2018-01-23 18:03:13 +00:00
|
|
|
return (err);
|
2016-04-29 22:42:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2018-01-23 18:03:13 +00:00
|
|
|
static void
|
|
|
|
fdt_print_overlay_load_error(int err, const char *filename)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
case FDT_ERR_NOTFOUND:
|
|
|
|
printf("%s: failed to load file\n", filename);
|
|
|
|
break;
|
|
|
|
case -FDT_ERR_BADVERSION:
|
|
|
|
printf("%s: incompatible blob version: %d, should be: %d\n",
|
|
|
|
filename, fdt_version(fdtp),
|
|
|
|
FDT_LAST_SUPPORTED_VERSION);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* libfdt errs are negative */
|
|
|
|
if (err < 0)
|
|
|
|
printf("%s: error validating blob: %s\n",
|
|
|
|
filename, fdt_strerror(err));
|
|
|
|
else
|
|
|
|
printf("%s: unknown load error\n", filename);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
stand/fdt: Consolidate overlay handling a little further
This should have been done as part of r327350, but due to lack of foresight
it came later. In the different places we apply overlays, we duplicate the
bits that check for fdt_overlays in the environment and supplement that with
any other places we need to check for overlays to load. These "other places"
will be loader specific and are not candidates for consolidation.
Provide an fdt_load_dtb_overlays to capture the common logic, allow passing
in an additional list of overlays to be loaded. This additional list of
overlays is used in practice for ubldr to pull in any fdt_overlays passed to
it from U-Boot environment, but it can be used for any other source of
overlays.
These additional overlays supplement loader.conf(5) fdt_overlays, rather
than replace, so that we're not restricted to specifying overlays in only
one place. This is a change from previous behavior where loader.conf(5)
supplied fdt_overlays would cause us to ignore U-Boot environment, and this
seems nonsensical- user should have sufficient control over both of these
aspects, or lack of control for good reasons.
A knob could be considered in the future to ignore U-Boot supplied overlays,
but the supplemental treatment seems like a good start.
Reviewed by: imp (earlier version), gonzo (earlier version)
Differential Revision: https://reviews.freebsd.org/D13993
2018-01-28 01:22:15 +00:00
|
|
|
static int
|
|
|
|
fdt_load_dtb_overlays_string(const char * filenames)
|
2016-04-29 22:42:59 +00:00
|
|
|
{
|
|
|
|
char *names;
|
2018-01-23 18:03:13 +00:00
|
|
|
char *name, *name_ext;
|
2016-04-29 22:42:59 +00:00
|
|
|
char *comaptr;
|
2018-01-23 18:03:13 +00:00
|
|
|
int err, namesz;
|
2016-04-29 22:42:59 +00:00
|
|
|
|
stand/fdt: Consolidate overlay handling a little further
This should have been done as part of r327350, but due to lack of foresight
it came later. In the different places we apply overlays, we duplicate the
bits that check for fdt_overlays in the environment and supplement that with
any other places we need to check for overlays to load. These "other places"
will be loader specific and are not candidates for consolidation.
Provide an fdt_load_dtb_overlays to capture the common logic, allow passing
in an additional list of overlays to be loaded. This additional list of
overlays is used in practice for ubldr to pull in any fdt_overlays passed to
it from U-Boot environment, but it can be used for any other source of
overlays.
These additional overlays supplement loader.conf(5) fdt_overlays, rather
than replace, so that we're not restricted to specifying overlays in only
one place. This is a change from previous behavior where loader.conf(5)
supplied fdt_overlays would cause us to ignore U-Boot environment, and this
seems nonsensical- user should have sufficient control over both of these
aspects, or lack of control for good reasons.
A knob could be considered in the future to ignore U-Boot supplied overlays,
but the supplemental treatment seems like a good start.
Reviewed by: imp (earlier version), gonzo (earlier version)
Differential Revision: https://reviews.freebsd.org/D13993
2018-01-28 01:22:15 +00:00
|
|
|
debugf("fdt_load_dtb_overlays_string(%s)\n", filenames);
|
2016-04-29 22:42:59 +00:00
|
|
|
|
|
|
|
names = strdup(filenames);
|
|
|
|
if (names == NULL)
|
|
|
|
return (1);
|
|
|
|
name = names;
|
|
|
|
do {
|
|
|
|
comaptr = strchr(name, ',');
|
|
|
|
if (comaptr)
|
|
|
|
*comaptr = '\0';
|
2018-01-23 18:03:13 +00:00
|
|
|
err = fdt_load_dtb_overlay(name);
|
|
|
|
if (err == FDT_ERR_NOTFOUND) {
|
|
|
|
/* Allocate enough to append ".dtbo" */
|
|
|
|
namesz = strlen(name) + 6;
|
|
|
|
name_ext = malloc(namesz);
|
|
|
|
if (name_ext == NULL) {
|
|
|
|
fdt_print_overlay_load_error(err, name);
|
|
|
|
name = comaptr + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
snprintf(name_ext, namesz, "%s.dtbo", name);
|
|
|
|
err = fdt_load_dtb_overlay(name_ext);
|
|
|
|
free(name_ext);
|
|
|
|
}
|
|
|
|
/* Catch error with either initial load or fallback load */
|
|
|
|
if (err != 0)
|
|
|
|
fdt_print_overlay_load_error(err, name);
|
2016-04-29 22:42:59 +00:00
|
|
|
name = comaptr + 1;
|
|
|
|
} while(comaptr);
|
|
|
|
|
|
|
|
free(names);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2018-01-28 03:07:22 +00:00
|
|
|
/*
|
|
|
|
* fdt_check_overlay_compatible - check that the overlay_fdt is compatible with
|
|
|
|
* base_fdt before we attempt to apply it. It will need to re-calculate offsets
|
|
|
|
* in the base every time, rather than trying to cache them earlier in the
|
|
|
|
* process, because the overlay application process can/will invalidate a lot of
|
|
|
|
* offsets.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fdt_check_overlay_compatible(void *base_fdt, void *overlay_fdt)
|
|
|
|
{
|
|
|
|
const char *compat;
|
|
|
|
int compat_len, ocompat_len;
|
|
|
|
int oroot_offset, root_offset;
|
|
|
|
int slidx, sllen;
|
|
|
|
|
|
|
|
oroot_offset = fdt_path_offset(overlay_fdt, "/");
|
|
|
|
if (oroot_offset < 0)
|
|
|
|
return (oroot_offset);
|
|
|
|
/*
|
|
|
|
* If /compatible in the overlay does not exist or if it is empty, then
|
|
|
|
* we're automatically compatible. We do this for the sake of rapid
|
|
|
|
* overlay development for overlays that aren't intended to be deployed.
|
|
|
|
* The user assumes the risk of using an overlay without /compatible.
|
|
|
|
*/
|
|
|
|
if (fdt_get_property(overlay_fdt, oroot_offset, "compatible",
|
|
|
|
&ocompat_len) == NULL || ocompat_len == 0)
|
|
|
|
return (0);
|
|
|
|
root_offset = fdt_path_offset(base_fdt, "/");
|
|
|
|
if (root_offset < 0)
|
|
|
|
return (root_offset);
|
|
|
|
/*
|
|
|
|
* However, an empty or missing /compatible on the base is an error,
|
|
|
|
* because allowing this offers no advantages.
|
|
|
|
*/
|
|
|
|
if (fdt_get_property(base_fdt, root_offset, "compatible",
|
|
|
|
&compat_len) == NULL)
|
|
|
|
return (compat_len);
|
|
|
|
else if(compat_len == 0)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
slidx = 0;
|
|
|
|
compat = fdt_stringlist_get(overlay_fdt, oroot_offset, "compatible",
|
|
|
|
slidx, &sllen);
|
|
|
|
while (compat != NULL) {
|
|
|
|
if (fdt_stringlist_search(base_fdt, root_offset, "compatible",
|
|
|
|
compat) >= 0)
|
|
|
|
return (0);
|
|
|
|
++slidx;
|
|
|
|
compat = fdt_stringlist_get(overlay_fdt, oroot_offset,
|
|
|
|
"compatible", slidx, &sllen);
|
|
|
|
};
|
|
|
|
|
|
|
|
/* We've exhausted the overlay's /compatible property... no match */
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2020-01-09 04:34:42 +00:00
|
|
|
/*
|
|
|
|
* Returns the number of overlays successfully applied
|
|
|
|
*/
|
|
|
|
int
|
2016-04-29 22:42:59 +00:00
|
|
|
fdt_apply_overlays()
|
|
|
|
{
|
|
|
|
struct preloaded_file *fp;
|
2018-01-15 05:00:26 +00:00
|
|
|
size_t max_overlay_size, next_fdtp_size;
|
|
|
|
size_t current_fdtp_size;
|
|
|
|
void *current_fdtp;
|
|
|
|
void *next_fdtp;
|
2016-04-29 22:42:59 +00:00
|
|
|
void *overlay;
|
2020-01-09 04:34:42 +00:00
|
|
|
int overlays_applied, rv;
|
2016-04-29 22:42:59 +00:00
|
|
|
|
|
|
|
if ((fdtp == NULL) || (fdtp_size == 0))
|
2020-01-09 04:34:42 +00:00
|
|
|
return (0);
|
2016-04-29 22:42:59 +00:00
|
|
|
|
2019-05-23 19:26:50 +00:00
|
|
|
if (fdt_overlays_applied)
|
2020-01-09 04:34:42 +00:00
|
|
|
return (0);
|
2019-05-23 19:26:50 +00:00
|
|
|
|
2016-04-29 22:42:59 +00:00
|
|
|
max_overlay_size = 0;
|
|
|
|
for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
|
|
|
|
if (max_overlay_size < fp->f_size)
|
|
|
|
max_overlay_size = fp->f_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Nothing to apply */
|
2018-01-15 05:00:26 +00:00
|
|
|
if (max_overlay_size == 0)
|
2020-01-09 04:34:42 +00:00
|
|
|
return (0);
|
2016-04-29 22:42:59 +00:00
|
|
|
|
|
|
|
overlay = malloc(max_overlay_size);
|
|
|
|
if (overlay == NULL) {
|
|
|
|
printf("failed to allocate memory for DTB blob with overlays\n");
|
2020-01-09 04:34:42 +00:00
|
|
|
return (0);
|
2016-04-29 22:42:59 +00:00
|
|
|
}
|
2018-01-15 05:00:26 +00:00
|
|
|
current_fdtp = fdtp;
|
|
|
|
current_fdtp_size = fdtp_size;
|
2020-01-09 04:34:42 +00:00
|
|
|
overlays_applied = 0;
|
2016-04-29 22:42:59 +00:00
|
|
|
for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) {
|
2018-01-28 03:07:22 +00:00
|
|
|
COPYOUT(fp->f_addr, overlay, fp->f_size);
|
|
|
|
/* Check compatible first to avoid unnecessary allocation */
|
|
|
|
rv = fdt_check_overlay_compatible(current_fdtp, overlay);
|
|
|
|
if (rv != 0) {
|
|
|
|
printf("DTB overlay '%s' not compatible\n", fp->f_name);
|
|
|
|
continue;
|
|
|
|
}
|
2016-04-29 22:42:59 +00:00
|
|
|
printf("applying DTB overlay '%s'\n", fp->f_name);
|
2018-01-15 05:00:26 +00:00
|
|
|
next_fdtp_size = current_fdtp_size + fp->f_size;
|
|
|
|
next_fdtp = malloc(next_fdtp_size);
|
|
|
|
if (next_fdtp == NULL) {
|
|
|
|
/*
|
|
|
|
* Output warning, then move on to applying other
|
|
|
|
* overlays in case this one is simply too large.
|
|
|
|
*/
|
|
|
|
printf("failed to allocate memory for overlay base\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rv = fdt_open_into(current_fdtp, next_fdtp, next_fdtp_size);
|
|
|
|
if (rv != 0) {
|
2018-01-15 18:08:01 +00:00
|
|
|
free(next_fdtp);
|
2018-01-15 05:00:26 +00:00
|
|
|
printf("failed to open base dtb into overlay base\n");
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-30 03:31:40 +00:00
|
|
|
/* Both overlay and next_fdtp may be modified in place */
|
2018-01-15 05:00:26 +00:00
|
|
|
rv = fdt_overlay_apply(next_fdtp, overlay);
|
|
|
|
if (rv == 0) {
|
|
|
|
/* Rotate next -> current */
|
|
|
|
if (current_fdtp != fdtp)
|
|
|
|
free(current_fdtp);
|
|
|
|
current_fdtp = next_fdtp;
|
2020-01-09 04:34:42 +00:00
|
|
|
fdt_pack(current_fdtp);
|
|
|
|
current_fdtp_size = fdt_totalsize(current_fdtp);
|
|
|
|
overlays_applied++;
|
2018-01-15 05:00:26 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Assume here that the base we tried to apply on is
|
|
|
|
* either trashed or in an inconsistent state. Trying to
|
|
|
|
* load it might work, but it's better to discard it and
|
|
|
|
* play it safe. */
|
|
|
|
free(next_fdtp);
|
|
|
|
printf("failed to apply overlay: %s\n",
|
|
|
|
fdt_strerror(rv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* We could have failed to apply all overlays; then we do nothing */
|
|
|
|
if (current_fdtp != fdtp) {
|
|
|
|
free(fdtp);
|
|
|
|
fdtp = current_fdtp;
|
|
|
|
fdtp_size = current_fdtp_size;
|
2016-04-29 22:42:59 +00:00
|
|
|
}
|
|
|
|
free(overlay);
|
2019-05-23 19:26:50 +00:00
|
|
|
fdt_overlays_applied = 1;
|
2020-01-09 04:34:42 +00:00
|
|
|
return (overlays_applied);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fdt_pad_dtb(size_t padding)
|
|
|
|
{
|
|
|
|
void *padded_fdtp;
|
|
|
|
size_t padded_fdtp_size;
|
|
|
|
|
|
|
|
padded_fdtp_size = fdtp_size + padding;
|
|
|
|
padded_fdtp = malloc(padded_fdtp_size);
|
|
|
|
if (padded_fdtp == NULL)
|
|
|
|
return (1);
|
|
|
|
if (fdt_open_into(fdtp, padded_fdtp, padded_fdtp_size) != 0) {
|
|
|
|
free(padded_fdtp);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
fdtp = padded_fdtp;
|
|
|
|
fdtp_size = padded_fdtp_size;
|
|
|
|
return (0);
|
2016-04-29 22:42:59 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 20:08:43 +00:00
|
|
|
int
|
|
|
|
fdt_is_setup(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (fdtp != NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2014-03-01 19:02:00 +00:00
|
|
|
int
|
2013-02-20 16:32:38 +00:00
|
|
|
fdt_setup_fdtp()
|
|
|
|
{
|
2014-02-22 03:29:53 +00:00
|
|
|
struct preloaded_file *bfp;
|
|
|
|
vm_offset_t va;
|
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
debugf("fdt_setup_fdtp()\n");
|
|
|
|
|
|
|
|
/* If we already loaded a file, use it. */
|
2014-02-22 03:29:53 +00:00
|
|
|
if ((bfp = file_findfile(NULL, "dtb")) != NULL) {
|
2014-02-22 19:10:58 +00:00
|
|
|
if (fdt_load_dtb(bfp->f_addr) == 0) {
|
2014-02-22 22:18:20 +00:00
|
|
|
printf("Using DTB from loaded file '%s'.\n",
|
|
|
|
bfp->f_name);
|
2019-04-11 13:26:28 +00:00
|
|
|
fdt_platform_load_overlays();
|
2014-02-22 19:10:58 +00:00
|
|
|
return (0);
|
|
|
|
}
|
2014-02-22 03:29:53 +00:00
|
|
|
}
|
2014-02-22 19:10:58 +00:00
|
|
|
|
|
|
|
/* If we were given the address of a valid blob in memory, use it. */
|
2014-02-22 03:29:53 +00:00
|
|
|
if (fdt_to_load != NULL) {
|
2014-02-22 19:10:58 +00:00
|
|
|
if (fdt_load_dtb_addr(fdt_to_load) == 0) {
|
2016-07-08 22:32:03 +00:00
|
|
|
printf("Using DTB from memory address %p.\n",
|
2016-02-29 07:27:49 +00:00
|
|
|
fdt_to_load);
|
2019-04-11 13:26:28 +00:00
|
|
|
fdt_platform_load_overlays();
|
2014-02-22 19:10:58 +00:00
|
|
|
return (0);
|
|
|
|
}
|
2014-02-22 03:29:53 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 13:26:28 +00:00
|
|
|
if (fdt_platform_load_dtb() == 0) {
|
|
|
|
fdt_platform_load_overlays();
|
2014-11-01 17:12:44 +00:00
|
|
|
return (0);
|
2019-04-11 13:26:28 +00:00
|
|
|
}
|
2014-02-22 22:18:20 +00:00
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
/* If there is a dtb compiled into the kernel, use it. */
|
2014-02-22 03:29:53 +00:00
|
|
|
if ((va = fdt_find_static_dtb()) != 0) {
|
2014-02-22 19:10:58 +00:00
|
|
|
if (fdt_load_dtb(va) == 0) {
|
|
|
|
printf("Using DTB compiled into kernel.\n");
|
|
|
|
return (0);
|
|
|
|
}
|
2014-02-22 03:29:53 +00:00
|
|
|
}
|
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
command_errmsg = "No device tree blob found!\n";
|
2014-02-22 03:29:53 +00:00
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
|
|
|
|
(cellbuf), (lim), (cellsize), 0);
|
|
|
|
|
|
|
|
/* Force using base 16 */
|
|
|
|
#define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \
|
|
|
|
(cellbuf), (lim), (cellsize), 16);
|
|
|
|
|
|
|
|
static int
|
2014-10-31 18:20:39 +00:00
|
|
|
_fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize,
|
2010-05-25 15:21:39 +00:00
|
|
|
uint8_t base)
|
|
|
|
{
|
2014-10-31 18:20:39 +00:00
|
|
|
const char *buf = str;
|
|
|
|
const char *end = str + strlen(str) - 2;
|
2010-05-25 15:21:39 +00:00
|
|
|
uint32_t *u32buf = NULL;
|
|
|
|
uint8_t *u8buf = NULL;
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
if (cellsize == sizeof(uint32_t))
|
|
|
|
u32buf = (uint32_t *)cellbuf;
|
|
|
|
else
|
|
|
|
u8buf = (uint8_t *)cellbuf;
|
|
|
|
|
|
|
|
if (lim == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
while (buf < end) {
|
|
|
|
|
|
|
|
/* Skip white whitespace(s)/separators */
|
|
|
|
while (!isxdigit(*buf) && buf < end)
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
if (u32buf != NULL)
|
|
|
|
u32buf[cnt] =
|
|
|
|
cpu_to_fdt32((uint32_t)strtol(buf, NULL, base));
|
|
|
|
|
|
|
|
else
|
|
|
|
u8buf[cnt] = (uint8_t)strtol(buf, NULL, base);
|
|
|
|
|
|
|
|
if (cnt + 1 <= lim - 1)
|
|
|
|
cnt++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
buf++;
|
|
|
|
/* Find another number */
|
|
|
|
while ((isxdigit(*buf) || *buf == 'x') && buf < end)
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
return (cnt);
|
|
|
|
}
|
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
void
|
|
|
|
fdt_fixup_ethernet(const char *str, char *ethstr, int len)
|
2010-05-25 15:21:39 +00:00
|
|
|
{
|
|
|
|
uint8_t tmp_addr[6];
|
|
|
|
|
|
|
|
/* Convert macaddr string into a vector of uints */
|
|
|
|
fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t));
|
|
|
|
/* Set actual property to a value from vect */
|
|
|
|
fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr),
|
|
|
|
"local-mac-address", &tmp_addr, 6 * sizeof(uint8_t));
|
|
|
|
}
|
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
void
|
|
|
|
fdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq)
|
2010-05-25 15:21:39 +00:00
|
|
|
{
|
|
|
|
int lo, o = 0, o2, maxo = 0, depth;
|
|
|
|
const uint32_t zero = 0;
|
|
|
|
|
|
|
|
/* We want to modify every subnode of /cpus */
|
|
|
|
o = fdt_path_offset(fdtp, "/cpus");
|
2012-05-11 04:18:39 +00:00
|
|
|
if (o < 0)
|
|
|
|
return;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
/* maxo should contain offset of node next to /cpus */
|
|
|
|
depth = 0;
|
|
|
|
maxo = o;
|
|
|
|
while (depth != -1)
|
|
|
|
maxo = fdt_next_node(fdtp, maxo, &depth);
|
|
|
|
|
|
|
|
/* Find CPU frequency properties */
|
|
|
|
o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency",
|
|
|
|
&zero, sizeof(uint32_t));
|
|
|
|
|
|
|
|
o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero,
|
|
|
|
sizeof(uint32_t));
|
|
|
|
|
|
|
|
lo = MIN(o, o2);
|
|
|
|
|
|
|
|
while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) {
|
|
|
|
|
|
|
|
o = fdt_node_offset_by_prop_value(fdtp, lo,
|
|
|
|
"clock-frequency", &zero, sizeof(uint32_t));
|
|
|
|
|
|
|
|
o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency",
|
|
|
|
&zero, sizeof(uint32_t));
|
|
|
|
|
|
|
|
/* We're only interested in /cpus subnode(s) */
|
|
|
|
if (lo > maxo)
|
|
|
|
break;
|
|
|
|
|
|
|
|
fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency",
|
|
|
|
(uint32_t)cpufreq);
|
|
|
|
|
|
|
|
fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency",
|
|
|
|
(uint32_t)busfreq);
|
|
|
|
|
|
|
|
lo = MIN(o, o2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-29 07:27:49 +00:00
|
|
|
#ifdef notyet
|
2013-02-25 01:50:04 +00:00
|
|
|
static int
|
2010-05-25 15:21:39 +00:00
|
|
|
fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells)
|
|
|
|
{
|
|
|
|
int cells_in_tuple, i, tuples, tuple_size;
|
|
|
|
uint32_t cur_start, cur_size;
|
|
|
|
|
|
|
|
cells_in_tuple = (addr_cells + size_cells);
|
|
|
|
tuple_size = cells_in_tuple * sizeof(uint32_t);
|
|
|
|
tuples = len / tuple_size;
|
|
|
|
if (tuples == 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
for (i = 0; i < tuples; i++) {
|
|
|
|
if (addr_cells == 2)
|
|
|
|
cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]);
|
|
|
|
else
|
|
|
|
cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]);
|
|
|
|
|
|
|
|
if (size_cells == 2)
|
|
|
|
cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]);
|
|
|
|
else
|
|
|
|
cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]);
|
|
|
|
|
|
|
|
if (cur_size == 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
|
|
|
|
i, cur_start, cur_size);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
2016-02-29 07:27:49 +00:00
|
|
|
#endif
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
void
|
|
|
|
fdt_fixup_memory(struct fdt_mem_region *region, size_t num)
|
2010-05-25 15:21:39 +00:00
|
|
|
{
|
2014-11-01 17:12:44 +00:00
|
|
|
struct fdt_mem_region *curmr;
|
2010-05-25 15:21:39 +00:00
|
|
|
uint32_t addr_cells, size_cells;
|
2016-01-14 21:39:10 +00:00
|
|
|
uint32_t *addr_cellsp, *size_cellsp;
|
2014-11-01 17:12:44 +00:00
|
|
|
int err, i, len, memory, root;
|
|
|
|
size_t realmrno;
|
2010-05-25 15:21:39 +00:00
|
|
|
uint8_t *buf, *sb;
|
2012-11-30 03:15:50 +00:00
|
|
|
uint64_t rstart, rsize;
|
|
|
|
int reserved;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
root = fdt_path_offset(fdtp, "/");
|
|
|
|
if (root < 0) {
|
|
|
|
sprintf(command_errbuf, "Could not find root node !");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memory = fdt_path_offset(fdtp, "/memory");
|
|
|
|
if (memory <= 0) {
|
|
|
|
/* Create proper '/memory' node. */
|
|
|
|
memory = fdt_add_subnode(fdtp, root, "memory");
|
|
|
|
if (memory <= 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"Could not fixup '/memory' "
|
2010-05-25 15:21:39 +00:00
|
|
|
"node, error code : %d!\n", memory);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = fdt_setprop(fdtp, memory, "device_type", "memory",
|
|
|
|
sizeof("memory"));
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells",
|
|
|
|
NULL);
|
|
|
|
size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL);
|
|
|
|
|
|
|
|
if (addr_cellsp == NULL || size_cellsp == NULL) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"Could not fixup '/memory' node : "
|
2010-05-25 15:21:39 +00:00
|
|
|
"%s %s property not found in root node!\n",
|
|
|
|
(!addr_cellsp) ? "#address-cells" : "",
|
|
|
|
(!size_cellsp) ? "#size-cells" : "");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_cells = fdt32_to_cpu(*addr_cellsp);
|
|
|
|
size_cells = fdt32_to_cpu(*size_cellsp);
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
/*
|
|
|
|
* Convert memreserve data to memreserve property
|
|
|
|
* Check if property already exists
|
|
|
|
*/
|
|
|
|
reserved = fdt_num_mem_rsv(fdtp);
|
|
|
|
if (reserved &&
|
|
|
|
(fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) {
|
|
|
|
len = (addr_cells + size_cells) * reserved * sizeof(uint32_t);
|
|
|
|
sb = buf = (uint8_t *)malloc(len);
|
|
|
|
if (!buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bzero(buf, len);
|
|
|
|
|
|
|
|
for (i = 0; i < reserved; i++) {
|
|
|
|
if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize))
|
|
|
|
break;
|
|
|
|
if (rsize) {
|
2016-04-30 00:26:38 +00:00
|
|
|
/* Ensure endianness, and put cells into a buffer */
|
2012-11-30 03:15:50 +00:00
|
|
|
if (addr_cells == 2)
|
|
|
|
*(uint64_t *)buf =
|
|
|
|
cpu_to_fdt64(rstart);
|
|
|
|
else
|
|
|
|
*(uint32_t *)buf =
|
|
|
|
cpu_to_fdt32(rstart);
|
|
|
|
|
|
|
|
buf += sizeof(uint32_t) * addr_cells;
|
|
|
|
if (size_cells == 2)
|
|
|
|
*(uint64_t *)buf =
|
|
|
|
cpu_to_fdt64(rsize);
|
|
|
|
else
|
|
|
|
*(uint32_t *)buf =
|
|
|
|
cpu_to_fdt32(rsize);
|
|
|
|
|
|
|
|
buf += sizeof(uint32_t) * size_cells;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set property */
|
|
|
|
if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0)
|
|
|
|
printf("Could not fixup 'memreserve' property.\n");
|
|
|
|
|
|
|
|
free(sb);
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
/* Count valid memory regions entries in sysinfo. */
|
2014-11-01 17:12:44 +00:00
|
|
|
realmrno = num;
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
if (region[i].start == 0 && region[i].size == 0)
|
2010-05-25 15:21:39 +00:00
|
|
|
realmrno--;
|
|
|
|
|
|
|
|
if (realmrno == 0) {
|
|
|
|
sprintf(command_errbuf, "Could not fixup '/memory' node : "
|
|
|
|
"sysinfo doesn't contain valid memory regions info!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t);
|
|
|
|
sb = buf = (uint8_t *)malloc(len);
|
|
|
|
if (!buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bzero(buf, len);
|
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
curmr = ®ion[i];
|
2010-05-25 15:21:39 +00:00
|
|
|
if (curmr->size != 0) {
|
2016-04-30 00:26:38 +00:00
|
|
|
/* Ensure endianness, and put cells into a buffer */
|
2010-05-25 15:21:39 +00:00
|
|
|
if (addr_cells == 2)
|
|
|
|
*(uint64_t *)buf =
|
|
|
|
cpu_to_fdt64(curmr->start);
|
|
|
|
else
|
|
|
|
*(uint32_t *)buf =
|
|
|
|
cpu_to_fdt32(curmr->start);
|
|
|
|
|
|
|
|
buf += sizeof(uint32_t) * addr_cells;
|
|
|
|
if (size_cells == 2)
|
|
|
|
*(uint64_t *)buf =
|
|
|
|
cpu_to_fdt64(curmr->size);
|
|
|
|
else
|
|
|
|
*(uint32_t *)buf =
|
|
|
|
cpu_to_fdt32(curmr->size);
|
|
|
|
|
|
|
|
buf += sizeof(uint32_t) * size_cells;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set property */
|
|
|
|
if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0)
|
|
|
|
sprintf(command_errbuf, "Could not fixup '/memory' node.\n");
|
2012-11-30 03:15:50 +00:00
|
|
|
|
|
|
|
free(sb);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
void
|
|
|
|
fdt_fixup_stdout(const char *str)
|
2010-05-25 15:21:39 +00:00
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
int len, no, sero;
|
|
|
|
const struct fdt_property *prop;
|
|
|
|
char *tmp[10];
|
|
|
|
|
|
|
|
ptr = (char *)str + strlen(str) - 1;
|
|
|
|
while (ptr > str && isdigit(*(str - 1)))
|
|
|
|
str--;
|
|
|
|
|
|
|
|
if (ptr == str)
|
|
|
|
return;
|
|
|
|
|
|
|
|
no = fdt_path_offset(fdtp, "/chosen");
|
|
|
|
if (no < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
prop = fdt_get_property(fdtp, no, "stdout", &len);
|
|
|
|
|
|
|
|
/* If /chosen/stdout does not extist, create it */
|
|
|
|
if (prop == NULL || (prop != NULL && len == 0)) {
|
|
|
|
|
|
|
|
bzero(tmp, 10 * sizeof(char));
|
|
|
|
strcpy((char *)&tmp, "serial");
|
|
|
|
if (strlen(ptr) > 3)
|
|
|
|
/* Serial number too long */
|
|
|
|
return;
|
|
|
|
|
|
|
|
strncpy((char *)tmp + 6, ptr, 3);
|
|
|
|
sero = fdt_path_offset(fdtp, (const char *)tmp);
|
|
|
|
if (sero < 0)
|
|
|
|
/*
|
|
|
|
* If serial device we're trying to assign
|
|
|
|
* stdout to doesn't exist in DT -- return.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
|
|
|
|
fdt_setprop(fdtp, no, "stdout", &tmp,
|
|
|
|
strlen((char *)&tmp) + 1);
|
|
|
|
fdt_setprop(fdtp, no, "stdin", &tmp,
|
|
|
|
strlen((char *)&tmp) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
stand/fdt: Consolidate overlay handling a little further
This should have been done as part of r327350, but due to lack of foresight
it came later. In the different places we apply overlays, we duplicate the
bits that check for fdt_overlays in the environment and supplement that with
any other places we need to check for overlays to load. These "other places"
will be loader specific and are not candidates for consolidation.
Provide an fdt_load_dtb_overlays to capture the common logic, allow passing
in an additional list of overlays to be loaded. This additional list of
overlays is used in practice for ubldr to pull in any fdt_overlays passed to
it from U-Boot environment, but it can be used for any other source of
overlays.
These additional overlays supplement loader.conf(5) fdt_overlays, rather
than replace, so that we're not restricted to specifying overlays in only
one place. This is a change from previous behavior where loader.conf(5)
supplied fdt_overlays would cause us to ignore U-Boot environment, and this
seems nonsensical- user should have sufficient control over both of these
aspects, or lack of control for good reasons.
A knob could be considered in the future to ignore U-Boot supplied overlays,
but the supplemental treatment seems like a good start.
Reviewed by: imp (earlier version), gonzo (earlier version)
Differential Revision: https://reviews.freebsd.org/D13993
2018-01-28 01:22:15 +00:00
|
|
|
void
|
|
|
|
fdt_load_dtb_overlays(const char *extras)
|
|
|
|
{
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
/* Any extra overlays supplied by pre-loader environment */
|
|
|
|
if (extras != NULL && *extras != '\0') {
|
|
|
|
printf("Loading DTB overlays: '%s'\n", extras);
|
|
|
|
fdt_load_dtb_overlays_string(extras);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Any overlays supplied by loader environment */
|
|
|
|
s = getenv("fdt_overlays");
|
|
|
|
if (s != NULL && *s != '\0') {
|
|
|
|
printf("Loading DTB overlays: '%s'\n", s);
|
|
|
|
fdt_load_dtb_overlays_string(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-20 13:08:57 +00:00
|
|
|
/*
|
|
|
|
* Locate the blob, fix it up and return its location.
|
|
|
|
*/
|
2013-02-25 01:50:04 +00:00
|
|
|
static int
|
2010-05-25 15:21:39 +00:00
|
|
|
fdt_fixup(void)
|
|
|
|
{
|
2018-08-23 18:01:34 +00:00
|
|
|
int chosen;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2014-02-22 19:10:58 +00:00
|
|
|
debugf("fdt_fixup()\n");
|
|
|
|
|
2014-02-20 13:09:08 +00:00
|
|
|
if (fdtp == NULL && fdt_setup_fdtp() != 0)
|
|
|
|
return (0);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
/* Create /chosen node (if not exists) */
|
|
|
|
if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) ==
|
|
|
|
-FDT_ERR_NOTFOUND)
|
|
|
|
chosen = fdt_add_subnode(fdtp, 0, "chosen");
|
|
|
|
|
|
|
|
/* Value assigned to fixup-applied does not matter. */
|
|
|
|
if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL))
|
2013-02-25 01:50:04 +00:00
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2014-11-01 17:12:44 +00:00
|
|
|
fdt_platform_fixups();
|
2010-05-25 15:21:39 +00:00
|
|
|
|
2018-08-23 01:22:13 +00:00
|
|
|
/*
|
|
|
|
* Re-fetch the /chosen subnode; our fixups may apply overlays or add
|
|
|
|
* nodes/properties that invalidate the offset we grabbed or created
|
|
|
|
* above, so we can no longer trust it.
|
|
|
|
*/
|
|
|
|
chosen = fdt_subnode_offset(fdtp, 0, "chosen");
|
2010-05-25 15:21:39 +00:00
|
|
|
fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0);
|
2013-02-25 01:50:04 +00:00
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
/*
|
2013-02-25 01:50:04 +00:00
|
|
|
* Copy DTB blob to specified location and return size
|
2012-11-30 03:15:50 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
fdt_copy(vm_offset_t va)
|
|
|
|
{
|
|
|
|
int err;
|
2014-02-22 19:10:58 +00:00
|
|
|
debugf("fdt_copy va 0x%08x\n", va);
|
2012-11-30 03:15:50 +00:00
|
|
|
if (fdtp == NULL) {
|
|
|
|
err = fdt_setup_fdtp();
|
|
|
|
if (err) {
|
2014-02-20 13:09:08 +00:00
|
|
|
printf("No valid device tree blob found!\n");
|
2012-11-30 03:15:50 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fdt_fixup() == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
COPYIN(fdtp, va, fdtp_size);
|
|
|
|
return (fdtp_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
int
|
|
|
|
command_fdt_internal(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
cmdf_t *cmdh;
|
2012-11-30 03:15:50 +00:00
|
|
|
int flags;
|
2010-05-25 15:21:39 +00:00
|
|
|
int i, err;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
command_errmsg = "usage is 'fdt <command> [<args>]";
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate fdt <command>.
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
cmdh = NULL;
|
|
|
|
while (!(commands[i].name == NULL)) {
|
2018-08-23 18:01:34 +00:00
|
|
|
if (strcmp(argv[1], commands[i].name) == 0) {
|
2010-05-25 15:21:39 +00:00
|
|
|
/* found it */
|
|
|
|
cmdh = commands[i].handler;
|
2012-11-30 03:15:50 +00:00
|
|
|
flags = commands[i].flags;
|
2010-05-25 15:21:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (cmdh == NULL) {
|
|
|
|
command_errmsg = "unknown command";
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
if (flags & CMD_REQUIRES_BLOB) {
|
|
|
|
/*
|
|
|
|
* Check if uboot env vars were parsed already. If not, do it now.
|
|
|
|
*/
|
|
|
|
if (fdt_fixup() == 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
/*
|
|
|
|
* Call command handler.
|
|
|
|
*/
|
|
|
|
err = (*cmdh)(argc, argv);
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
static int
|
|
|
|
fdt_cmd_addr(int argc, char *argv[])
|
|
|
|
{
|
2013-02-23 20:34:47 +00:00
|
|
|
struct preloaded_file *fp;
|
2013-02-20 16:32:38 +00:00
|
|
|
struct fdt_header *hdr;
|
2013-02-23 20:34:47 +00:00
|
|
|
const char *addr;
|
|
|
|
char *cp;
|
|
|
|
|
|
|
|
fdt_to_load = NULL;
|
2012-11-30 03:15:50 +00:00
|
|
|
|
|
|
|
if (argc > 2)
|
|
|
|
addr = argv[2];
|
|
|
|
else {
|
|
|
|
sprintf(command_errbuf, "no address specified");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
2013-02-23 20:34:47 +00:00
|
|
|
hdr = (struct fdt_header *)strtoul(addr, &cp, 16);
|
2012-11-30 03:15:50 +00:00
|
|
|
if (cp == addr) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"Invalid address: %s", addr);
|
2012-11-30 03:15:50 +00:00
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
2013-02-23 20:34:47 +00:00
|
|
|
while ((fp = file_findfile(NULL, "dtb")) != NULL) {
|
|
|
|
file_discard(fp);
|
|
|
|
}
|
2012-11-30 03:15:50 +00:00
|
|
|
|
2013-02-23 20:34:47 +00:00
|
|
|
fdt_to_load = hdr;
|
2012-11-30 03:15:50 +00:00
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
static int
|
|
|
|
fdt_cmd_cd(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *path;
|
|
|
|
char tmp[FDT_CWD_LEN];
|
|
|
|
int len, o;
|
|
|
|
|
|
|
|
path = (argc > 2) ? argv[2] : "/";
|
|
|
|
|
|
|
|
if (path[0] == '/') {
|
|
|
|
len = strlen(path);
|
|
|
|
if (len >= FDT_CWD_LEN)
|
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
/* Handle path specification relative to cwd */
|
|
|
|
len = strlen(cwd) + strlen(path) + 1;
|
|
|
|
if (len >= FDT_CWD_LEN)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
strcpy(tmp, cwd);
|
|
|
|
strcat(tmp, "/");
|
|
|
|
strcat(tmp, path);
|
|
|
|
path = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
o = fdt_path_offset(fdtp, path);
|
|
|
|
if (o < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"could not find node: '%s'", path);
|
2010-05-25 15:21:39 +00:00
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(cwd, path);
|
|
|
|
return (CMD_OK);
|
|
|
|
|
|
|
|
fail:
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"path too long: %d, max allowed: %d", len, FDT_CWD_LEN - 1);
|
2010-05-25 15:21:39 +00:00
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_hdr(int argc __unused, char *argv[] __unused)
|
|
|
|
{
|
|
|
|
char line[80];
|
|
|
|
int ver;
|
|
|
|
|
|
|
|
if (fdtp == NULL) {
|
|
|
|
command_errmsg = "no device tree blob pointer?!";
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
ver = fdt_version(fdtp);
|
|
|
|
pager_open();
|
|
|
|
sprintf(line, "\nFlattened device tree header (%p):\n", fdtp);
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " magic = 0x%08x\n", fdt_magic(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " size = %d\n", fdt_totalsize(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " off_dt_struct = 0x%08x\n",
|
|
|
|
fdt_off_dt_struct(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " off_dt_strings = 0x%08x\n",
|
|
|
|
fdt_off_dt_strings(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " off_mem_rsvmap = 0x%08x\n",
|
|
|
|
fdt_off_mem_rsvmap(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " version = %d\n", ver);
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
sprintf(line, " last compatible version = %d\n",
|
|
|
|
fdt_last_comp_version(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
if (ver >= 2) {
|
|
|
|
sprintf(line, " boot_cpuid = %d\n",
|
|
|
|
fdt_boot_cpuid_phys(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
if (ver >= 3) {
|
|
|
|
sprintf(line, " size_dt_strings = %d\n",
|
|
|
|
fdt_size_dt_strings(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
if (ver >= 17) {
|
|
|
|
sprintf(line, " size_dt_struct = %d\n",
|
|
|
|
fdt_size_dt_struct(fdtp));
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
2016-05-18 05:59:05 +00:00
|
|
|
out:
|
2010-05-25 15:21:39 +00:00
|
|
|
pager_close();
|
|
|
|
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_ls(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
const char *prevname[FDT_MAX_DEPTH] = { NULL };
|
|
|
|
const char *name;
|
|
|
|
char *path;
|
2016-07-01 21:09:30 +00:00
|
|
|
int i, o, depth;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
path = (argc > 2) ? argv[2] : NULL;
|
|
|
|
if (path == NULL)
|
|
|
|
path = cwd;
|
|
|
|
|
|
|
|
o = fdt_path_offset(fdtp, path);
|
|
|
|
if (o < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"could not find node: '%s'", path);
|
2010-05-25 15:21:39 +00:00
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (depth = 0;
|
|
|
|
(o >= 0) && (depth >= 0);
|
|
|
|
o = fdt_next_node(fdtp, o, &depth)) {
|
|
|
|
|
2016-07-01 21:09:30 +00:00
|
|
|
name = fdt_get_name(fdtp, o, NULL);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
if (depth > FDT_MAX_DEPTH) {
|
|
|
|
printf("max depth exceeded: %d\n", depth);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
prevname[depth] = name;
|
|
|
|
|
|
|
|
/* Skip root (i = 1) when printing devices */
|
|
|
|
for (i = 1; i <= depth; i++) {
|
|
|
|
if (prevname[i] == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (strcmp(cwd, "/") == 0)
|
|
|
|
printf("/");
|
|
|
|
printf("%s", prevname[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline int
|
|
|
|
isprint(int c)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (c >= ' ' && c <= 0x7e);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_isprint(const void *data, int len, int *count)
|
|
|
|
{
|
|
|
|
const char *d;
|
|
|
|
char ch;
|
|
|
|
int yesno, i;
|
|
|
|
|
|
|
|
if (len == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
d = (const char *)data;
|
|
|
|
if (d[len - 1] != '\0')
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
*count = 0;
|
|
|
|
yesno = 1;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
ch = *(d + i);
|
|
|
|
if (isprint(ch) || (ch == '\0' && i > 0)) {
|
|
|
|
/* Count strings */
|
|
|
|
if (ch == '\0')
|
|
|
|
(*count)++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
yesno = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (yesno);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_data_str(const void *data, int len, int count, char **buf)
|
|
|
|
{
|
2012-03-22 20:34:26 +00:00
|
|
|
char *b, *tmp;
|
2010-05-25 15:21:39 +00:00
|
|
|
const char *d;
|
2012-03-22 20:34:26 +00:00
|
|
|
int buf_len, i, l;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the length for the string and allocate memory.
|
|
|
|
*
|
2012-03-22 20:34:26 +00:00
|
|
|
* Note that 'len' already includes at least one terminator.
|
2010-05-25 15:21:39 +00:00
|
|
|
*/
|
2012-03-22 20:34:26 +00:00
|
|
|
buf_len = len;
|
2010-05-25 15:21:39 +00:00
|
|
|
if (count > 1) {
|
|
|
|
/*
|
|
|
|
* Each token had already a terminator buried in 'len', but we
|
|
|
|
* only need one eventually, don't count space for these.
|
|
|
|
*/
|
2012-03-22 20:34:26 +00:00
|
|
|
buf_len -= count - 1;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
/* Each consecutive token requires a ", " separator. */
|
2012-03-22 20:34:26 +00:00
|
|
|
buf_len += count * 2;
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
2012-03-22 20:34:26 +00:00
|
|
|
/* Add some space for surrounding double quotes. */
|
|
|
|
buf_len += count * 2;
|
|
|
|
|
|
|
|
/* Note that string being put in 'tmp' may be as big as 'buf_len'. */
|
|
|
|
b = (char *)malloc(buf_len);
|
|
|
|
tmp = (char *)malloc(buf_len);
|
2010-05-25 15:21:39 +00:00
|
|
|
if (b == NULL)
|
2012-03-22 20:34:26 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (tmp == NULL) {
|
|
|
|
free(b);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
b[0] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we have space, format the string.
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
d = (const char *)data + i;
|
|
|
|
l = strlen(d) + 1;
|
|
|
|
|
|
|
|
sprintf(tmp, "\"%s\"%s", d,
|
|
|
|
(i + l) < len ? ", " : "");
|
|
|
|
strcat(b, tmp);
|
|
|
|
|
|
|
|
i += l;
|
|
|
|
|
|
|
|
} while (i < len);
|
|
|
|
*buf = b;
|
|
|
|
|
2012-03-22 20:34:26 +00:00
|
|
|
free(tmp);
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
return (0);
|
2012-03-22 20:34:26 +00:00
|
|
|
error:
|
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_data_cell(const void *data, int len, char **buf)
|
|
|
|
{
|
2012-03-22 20:34:26 +00:00
|
|
|
char *b, *tmp;
|
2010-05-25 15:21:39 +00:00
|
|
|
const uint32_t *c;
|
|
|
|
int count, i, l;
|
|
|
|
|
|
|
|
/* Number of cells */
|
|
|
|
count = len / 4;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the length for the string and allocate memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Each byte translates to 2 output characters */
|
|
|
|
l = len * 2;
|
|
|
|
if (count > 1) {
|
|
|
|
/* Each consecutive cell requires a " " separator. */
|
|
|
|
l += (count - 1) * 1;
|
|
|
|
}
|
|
|
|
/* Each cell will have a "0x" prefix */
|
|
|
|
l += count * 2;
|
|
|
|
/* Space for surrounding <> and terminator */
|
|
|
|
l += 3;
|
|
|
|
|
|
|
|
b = (char *)malloc(l);
|
2012-03-22 20:34:26 +00:00
|
|
|
tmp = (char *)malloc(l);
|
2010-05-25 15:21:39 +00:00
|
|
|
if (b == NULL)
|
2012-03-22 20:34:26 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (tmp == NULL) {
|
|
|
|
free(b);
|
|
|
|
goto error;
|
|
|
|
}
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
b[0] = '\0';
|
|
|
|
strcat(b, "<");
|
|
|
|
|
|
|
|
for (i = 0; i < len; i += 4) {
|
|
|
|
c = (const uint32_t *)((const uint8_t *)data + i);
|
|
|
|
sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c),
|
|
|
|
i < (len - 4) ? " " : "");
|
|
|
|
strcat(b, tmp);
|
|
|
|
}
|
|
|
|
strcat(b, ">");
|
|
|
|
*buf = b;
|
|
|
|
|
2012-03-22 20:34:26 +00:00
|
|
|
free(tmp);
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
return (0);
|
2012-03-22 20:34:26 +00:00
|
|
|
error:
|
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_data_bytes(const void *data, int len, char **buf)
|
|
|
|
{
|
2012-03-22 20:34:26 +00:00
|
|
|
char *b, *tmp;
|
2010-05-25 15:21:39 +00:00
|
|
|
const char *d;
|
|
|
|
int i, l;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the length for the string and allocate memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Each byte translates to 2 output characters */
|
|
|
|
l = len * 2;
|
|
|
|
if (len > 1)
|
|
|
|
/* Each consecutive byte requires a " " separator. */
|
|
|
|
l += (len - 1) * 1;
|
|
|
|
/* Each byte will have a "0x" prefix */
|
|
|
|
l += len * 2;
|
|
|
|
/* Space for surrounding [] and terminator. */
|
|
|
|
l += 3;
|
|
|
|
|
|
|
|
b = (char *)malloc(l);
|
2012-03-22 20:34:26 +00:00
|
|
|
tmp = (char *)malloc(l);
|
2010-05-25 15:21:39 +00:00
|
|
|
if (b == NULL)
|
2012-03-22 20:34:26 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (tmp == NULL) {
|
|
|
|
free(b);
|
|
|
|
goto error;
|
|
|
|
}
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
b[0] = '\0';
|
|
|
|
strcat(b, "[");
|
|
|
|
|
|
|
|
for (i = 0, d = data; i < len; i++) {
|
|
|
|
sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : "");
|
|
|
|
strcat(b, tmp);
|
|
|
|
}
|
|
|
|
strcat(b, "]");
|
|
|
|
*buf = b;
|
|
|
|
|
2012-03-22 20:34:26 +00:00
|
|
|
free(tmp);
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
return (0);
|
2012-03-22 20:34:26 +00:00
|
|
|
error:
|
|
|
|
return (1);
|
2010-05-25 15:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_data_fmt(const void *data, int len, char **buf)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
*buf = NULL;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fdt_isprint(data, len, &count))
|
|
|
|
return (fdt_data_str(data, len, count, buf));
|
|
|
|
|
|
|
|
else if ((len % 4) == 0)
|
|
|
|
return (fdt_data_cell(data, len, buf));
|
|
|
|
|
|
|
|
else
|
|
|
|
return (fdt_data_bytes(data, len, buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_prop(int offset)
|
|
|
|
{
|
|
|
|
char *line, *buf;
|
|
|
|
const struct fdt_property *prop;
|
|
|
|
const char *name;
|
|
|
|
const void *data;
|
|
|
|
int len, rv;
|
|
|
|
|
|
|
|
line = NULL;
|
|
|
|
prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop));
|
|
|
|
if (prop == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
|
|
|
|
len = fdt32_to_cpu(prop->len);
|
|
|
|
|
|
|
|
rv = 0;
|
|
|
|
buf = NULL;
|
|
|
|
if (len == 0) {
|
|
|
|
/* Property without value */
|
|
|
|
line = (char *)malloc(strlen(name) + 2);
|
|
|
|
if (line == NULL) {
|
|
|
|
rv = 2;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
sprintf(line, "%s\n", name);
|
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process property with value
|
|
|
|
*/
|
|
|
|
data = prop->data;
|
|
|
|
|
|
|
|
if (fdt_data_fmt(data, len, &buf) != 0) {
|
|
|
|
rv = 3;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) +
|
|
|
|
strlen(buf) + 2);
|
|
|
|
if (line == NULL) {
|
|
|
|
sprintf(command_errbuf, "could not allocate space for string");
|
|
|
|
rv = 4;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf);
|
|
|
|
|
|
|
|
out1:
|
|
|
|
pager_open();
|
|
|
|
pager_output(line);
|
|
|
|
pager_close();
|
|
|
|
|
|
|
|
out2:
|
|
|
|
if (buf)
|
|
|
|
free(buf);
|
|
|
|
|
|
|
|
if (line)
|
|
|
|
free(line);
|
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_modprop(int nodeoff, char *propname, void *value, char mode)
|
|
|
|
{
|
|
|
|
uint32_t cells[100];
|
2014-10-31 18:20:39 +00:00
|
|
|
const char *buf;
|
2010-05-25 15:21:39 +00:00
|
|
|
int len, rv;
|
|
|
|
const struct fdt_property *p;
|
|
|
|
|
|
|
|
p = fdt_get_property(fdtp, nodeoff, propname, NULL);
|
|
|
|
|
|
|
|
if (p != NULL) {
|
|
|
|
if (mode == 1) {
|
|
|
|
/* Adding inexistant value in mode 1 is forbidden */
|
|
|
|
sprintf(command_errbuf, "property already exists!");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
} else if (mode == 0) {
|
|
|
|
sprintf(command_errbuf, "property does not exist!");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
rv = 0;
|
2014-10-31 18:20:39 +00:00
|
|
|
buf = value;
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
switch (*buf) {
|
|
|
|
case '&':
|
|
|
|
/* phandles */
|
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
/* Data cells */
|
|
|
|
len = fdt_strtovect(buf, (void *)&cells, 100,
|
|
|
|
sizeof(uint32_t));
|
|
|
|
|
|
|
|
rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
|
|
|
|
len * sizeof(uint32_t));
|
|
|
|
break;
|
|
|
|
case '[':
|
|
|
|
/* Data bytes */
|
|
|
|
len = fdt_strtovect(buf, (void *)&cells, 100,
|
|
|
|
sizeof(uint8_t));
|
|
|
|
|
|
|
|
rv = fdt_setprop(fdtp, nodeoff, propname, &cells,
|
|
|
|
len * sizeof(uint8_t));
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
default:
|
|
|
|
/* Default -- string */
|
|
|
|
rv = fdt_setprop_string(fdtp, nodeoff, propname, value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-22 20:34:26 +00:00
|
|
|
if (rv != 0) {
|
|
|
|
if (rv == -FDT_ERR_NOSPACE)
|
|
|
|
sprintf(command_errbuf,
|
|
|
|
"Device tree blob is too small!\n");
|
|
|
|
else
|
|
|
|
sprintf(command_errbuf,
|
|
|
|
"Could not add/modify property!\n");
|
|
|
|
}
|
2010-05-25 15:21:39 +00:00
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Merge strings from argv into a single string */
|
|
|
|
static int
|
|
|
|
fdt_merge_strings(int argc, char *argv[], int start, char **buffer)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
int i, idx, sz;
|
|
|
|
|
|
|
|
*buffer = NULL;
|
|
|
|
sz = 0;
|
|
|
|
|
|
|
|
for (i = start; i < argc; i++)
|
|
|
|
sz += strlen(argv[i]);
|
|
|
|
|
|
|
|
/* Additional bytes for whitespaces between args */
|
|
|
|
sz += argc - start;
|
|
|
|
|
|
|
|
buf = (char *)malloc(sizeof(char) * sz);
|
|
|
|
if (buf == NULL) {
|
|
|
|
sprintf(command_errbuf, "could not allocate space "
|
|
|
|
"for string");
|
|
|
|
return (1);
|
|
|
|
}
|
2015-02-25 09:57:05 +00:00
|
|
|
bzero(buf, sizeof(char) * sz);
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
for (i = start, idx = 0; i < argc; i++) {
|
|
|
|
strcpy(buf + idx, argv[i]);
|
|
|
|
idx += strlen(argv[i]);
|
|
|
|
buf[idx] = ' ';
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
buf[sz - 1] = '\0';
|
|
|
|
*buffer = buf;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract offset and name of node/property from a given path */
|
|
|
|
static int
|
|
|
|
fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff)
|
|
|
|
{
|
|
|
|
int o;
|
|
|
|
char *path = *pathp, *name = NULL, *subpath = NULL;
|
|
|
|
|
|
|
|
subpath = strrchr(path, '/');
|
|
|
|
if (subpath == NULL) {
|
|
|
|
o = fdt_path_offset(fdtp, cwd);
|
|
|
|
name = path;
|
|
|
|
path = (char *)&cwd;
|
|
|
|
} else {
|
|
|
|
*subpath = '\0';
|
|
|
|
if (strlen(path) == 0)
|
|
|
|
path = cwd;
|
|
|
|
|
|
|
|
name = subpath + 1;
|
|
|
|
o = fdt_path_offset(fdtp, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(name) == 0) {
|
|
|
|
sprintf(command_errbuf, "name not specified");
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (o < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"could not find node: '%s'", path);
|
2010-05-25 15:21:39 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
*namep = name;
|
|
|
|
*nodeoff = o;
|
|
|
|
*pathp = path;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_prop(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *path, *propname, *value;
|
|
|
|
int o, next, depth, rv;
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
path = (argc > 2) ? argv[2] : NULL;
|
|
|
|
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
if (argc > 3) {
|
|
|
|
/* Merge property value strings into one */
|
|
|
|
if (fdt_merge_strings(argc, argv, 3, &value) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
} else
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
path = cwd;
|
|
|
|
|
|
|
|
rv = CMD_OK;
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
/* If value is specified -- try to modify prop. */
|
|
|
|
if (fdt_extract_nameloc(&path, &propname, &o) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
rv = fdt_modprop(o, propname, value, 0);
|
|
|
|
if (rv)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
return (CMD_OK);
|
|
|
|
|
|
|
|
}
|
|
|
|
/* User wants to display properties */
|
|
|
|
o = fdt_path_offset(fdtp, path);
|
|
|
|
|
|
|
|
if (o < 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"could not find node: '%s'", path);
|
2010-05-25 15:21:39 +00:00
|
|
|
rv = CMD_ERROR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
depth = 0;
|
|
|
|
while (depth >= 0) {
|
|
|
|
tag = fdt_next_tag(fdtp, o, &next);
|
|
|
|
switch (tag) {
|
|
|
|
case FDT_NOP:
|
|
|
|
break;
|
|
|
|
case FDT_PROP:
|
|
|
|
if (depth > 1)
|
|
|
|
/* Don't process properties of nested nodes */
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (fdt_prop(o) != 0) {
|
|
|
|
sprintf(command_errbuf, "could not process "
|
|
|
|
"property");
|
|
|
|
rv = CMD_ERROR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FDT_BEGIN_NODE:
|
|
|
|
depth++;
|
|
|
|
if (depth > FDT_MAX_DEPTH) {
|
|
|
|
printf("warning: nesting too deep: %d\n",
|
|
|
|
depth);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FDT_END_NODE:
|
|
|
|
depth--;
|
|
|
|
if (depth == 0)
|
|
|
|
/*
|
|
|
|
* This is the end of our starting node, force
|
|
|
|
* the loop finish.
|
|
|
|
*/
|
|
|
|
depth--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
o = next;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_mkprop(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int o;
|
|
|
|
char *path, *propname, *value;
|
|
|
|
|
|
|
|
path = (argc > 2) ? argv[2] : NULL;
|
|
|
|
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
if (argc > 3) {
|
|
|
|
/* Merge property value strings into one */
|
|
|
|
if (fdt_merge_strings(argc, argv, 3, &value) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
} else
|
|
|
|
value = NULL;
|
|
|
|
|
|
|
|
if (fdt_extract_nameloc(&path, &propname, &o) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
if (fdt_modprop(o, propname, value, 1))
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_rm(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int o, rv;
|
|
|
|
char *path = NULL, *propname;
|
|
|
|
|
|
|
|
if (argc > 2)
|
|
|
|
path = argv[2];
|
|
|
|
else {
|
|
|
|
sprintf(command_errbuf, "no node/property name specified");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
o = fdt_path_offset(fdtp, path);
|
|
|
|
if (o < 0) {
|
|
|
|
/* If node not found -- try to find & delete property */
|
|
|
|
if (fdt_extract_nameloc(&path, &propname, &o) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
if ((rv = fdt_delprop(fdtp, o, propname)) != 0) {
|
2016-08-20 16:23:19 +00:00
|
|
|
snprintf(command_errbuf, sizeof(command_errbuf),
|
|
|
|
"could not delete %s\n",
|
|
|
|
(rv == -FDT_ERR_NOTFOUND) ?
|
2010-05-25 15:21:39 +00:00
|
|
|
"(property/node does not exist)" : "");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
} else
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
/* If node exists -- remove node */
|
|
|
|
rv = fdt_del_node(fdtp, o);
|
|
|
|
if (rv) {
|
|
|
|
sprintf(command_errbuf, "could not delete node");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_mknode(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int o, rv;
|
|
|
|
char *path = NULL, *nodename = NULL;
|
|
|
|
|
|
|
|
if (argc > 2)
|
|
|
|
path = argv[2];
|
|
|
|
else {
|
|
|
|
sprintf(command_errbuf, "no node name specified");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fdt_extract_nameloc(&path, &nodename, &o) != 0)
|
|
|
|
return (CMD_ERROR);
|
|
|
|
|
|
|
|
rv = fdt_add_subnode(fdtp, o, nodename);
|
|
|
|
|
|
|
|
if (rv < 0) {
|
2012-03-22 20:34:26 +00:00
|
|
|
if (rv == -FDT_ERR_NOSPACE)
|
|
|
|
sprintf(command_errbuf,
|
|
|
|
"Device tree blob is too small!\n");
|
|
|
|
else
|
|
|
|
sprintf(command_errbuf,
|
|
|
|
"Could not add node!\n");
|
2010-05-25 15:21:39 +00:00
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fdt_cmd_pwd(int argc, char *argv[])
|
|
|
|
{
|
2012-03-22 20:34:26 +00:00
|
|
|
char line[FDT_CWD_LEN];
|
2010-05-25 15:21:39 +00:00
|
|
|
|
|
|
|
pager_open();
|
|
|
|
sprintf(line, "%s\n", cwd);
|
|
|
|
pager_output(line);
|
|
|
|
pager_close();
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
2012-11-30 03:15:50 +00:00
|
|
|
static int
|
|
|
|
fdt_cmd_mres(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
uint64_t start, size;
|
|
|
|
int i, total;
|
|
|
|
char line[80];
|
|
|
|
|
|
|
|
pager_open();
|
|
|
|
total = fdt_num_mem_rsv(fdtp);
|
|
|
|
if (total > 0) {
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output("Reserved memory regions:\n"))
|
|
|
|
goto out;
|
2012-11-30 03:15:50 +00:00
|
|
|
for (i = 0; i < total; i++) {
|
|
|
|
fdt_get_mem_rsv(fdtp, i, &start, &size);
|
|
|
|
sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n",
|
|
|
|
i, start, size);
|
2016-05-18 05:59:05 +00:00
|
|
|
if (pager_output(line))
|
|
|
|
goto out;
|
2012-11-30 03:15:50 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
pager_output("No reserved memory regions\n");
|
2016-05-18 05:59:05 +00:00
|
|
|
out:
|
2012-11-30 03:15:50 +00:00
|
|
|
pager_close();
|
|
|
|
|
|
|
|
return (CMD_OK);
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:21:39 +00:00
|
|
|
static int
|
|
|
|
fdt_cmd_nyi(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
|
|
|
|
printf("command not yet implemented\n");
|
|
|
|
return (CMD_ERROR);
|
|
|
|
}
|
2019-05-23 19:26:50 +00:00
|
|
|
|
|
|
|
const char *
|
|
|
|
fdt_devmatch_next(int *tag, int *compatlen)
|
|
|
|
{
|
|
|
|
const struct fdt_property *p;
|
|
|
|
const struct fdt_property *status;
|
|
|
|
int o, len = -1;
|
|
|
|
static int depth = 0;
|
|
|
|
|
|
|
|
if (fdtp == NULL) {
|
|
|
|
fdt_setup_fdtp();
|
|
|
|
fdt_apply_overlays();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*tag != 0) {
|
|
|
|
o = *tag;
|
|
|
|
/* We are at the end of the DTB */
|
|
|
|
if (o < 0)
|
|
|
|
return (NULL);
|
|
|
|
} else {
|
|
|
|
o = fdt_path_offset(fdtp, "/");
|
|
|
|
if (o < 0) {
|
|
|
|
printf("Can't find dtb\n");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
depth = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the next node with a compatible property */
|
|
|
|
while (1) {
|
|
|
|
p = NULL;
|
|
|
|
if (o >= 0 && depth >= 0) {
|
|
|
|
/* skip disabled nodes */
|
|
|
|
status = fdt_get_property(fdtp, o, "status", &len);
|
|
|
|
if (len > 0) {
|
|
|
|
if (strcmp(status->data, "disabled") == 0) {
|
|
|
|
o = fdt_next_node(fdtp, o, &depth);
|
|
|
|
if (o < 0) /* End of tree */
|
|
|
|
return (NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = fdt_get_property(fdtp, o, "compatible", &len);
|
|
|
|
}
|
|
|
|
if (p)
|
|
|
|
break;
|
|
|
|
o = fdt_next_node(fdtp, o, &depth);
|
|
|
|
if (o < 0) /* End of tree */
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare next node for next call */
|
|
|
|
o = fdt_next_node(fdtp, o, &depth);
|
|
|
|
*tag = o;
|
|
|
|
|
|
|
|
if (len >= 0) {
|
|
|
|
*compatlen = len;
|
|
|
|
return (p->data);
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|