/*-
* 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
*
* 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.
*/
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <fdt.h>
#include <libfdt.h>
#include "bootstrap.h"
#include "fdt_platform.h"
#ifdef DEBUG
#else
#endif
/* Location of FDT yet to be loaded. */
/* This may be in read-only memory, so can't be manipulated directly. */
/* Location of FDT on heap. */
/* This is the copy we actually manipulate. */
/* Size of FDT blob */
/* Location of FDT in kernel or module. */
/* This won't be set if FDT is loaded from disk or memory. */
/* If it is set, we'll update it when fdt_copy() gets called. */
typedef int cmdf_t(int, char *[]);
struct cmdtab {
const char *name;
int flags;
};
{ "addr", &fdt_cmd_addr, 0 },
{ "alias", &fdt_cmd_nyi, 0 },
};
static vm_offset_t
{
char *strp;
int i, sym_count;
debugf("fdt_find_static_dtb()\n");
return (0);
/* Locate the dynamic symbols and strtab. */
return (0);
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.
*/
fdt_start = 0;
--sym_count;
continue;
}
return (fdt_start);
}
static int
{
int err;
if (err < 0) {
if (err == -FDT_ERR_BADVERSION) {
"incompatible blob version: %d, should be: %d",
} else {
}
return (1);
}
/*
* Release previous blob
*/
if (fdtp)
command_errmsg = "can't allocate memory for device tree copy";
return (1);
}
return (0);
}
int
{
int err;
if (err < 0) {
return (err);
}
command_errmsg = "can't allocate memory for device tree copy";
return (1);
}
fdtp_va = 0; // Don't write this back into module or kernel.
return (0);
}
int
{
int err;
/* Attempt to load and validate a new dtb from a file. */
"failed to load file '%s'", filename);
return (1);
}
return (err);
}
/* A new dtb was validated, discard any previous file. */
if (oldbfp)
return (0);
}
int
{
debugf("fdt_setup_fdtp()\n");
/* If we already loaded a file, use it. */
printf("Using DTB from loaded file '%s'.\n",
return (0);
}
}
/* If we were given the address of a valid blob in memory, use it. */
if (fdt_to_load != NULL) {
if (fdt_load_dtb_addr(fdt_to_load) == 0) {
printf("Using DTB from memory address 0x%p.\n",
return (0);
}
}
if (fdt_platform_load_dtb() == 0)
return (0);
/* If there is a dtb compiled into the kernel, use it. */
if ((va = fdt_find_static_dtb()) != 0) {
if (fdt_load_dtb(va) == 0) {
printf("Using DTB compiled into kernel.\n");
return (0);
}
}
command_errmsg = "No device tree blob found!\n";
return (1);
}
/* Force using base 16 */
static int
{
int cnt = 0;
else
if (lim == 0)
return (0);
/* Skip white whitespace(s)/separators */
buf++;
else
cnt++;
else
break;
buf++;
/* Find another number */
buf++;
}
return (cnt);
}
void
{
/* Convert macaddr string into a vector of uints */
/* Set actual property to a value from vect */
}
void
{
/* We want to modify every subnode of /cpus */
if (o < 0)
return;
/* maxo should contain offset of node next to /cpus */
depth = 0;
maxo = o;
while (depth != -1)
/* Find CPU frequency properties */
sizeof(uint32_t));
/* We're only interested in /cpus subnode(s) */
break;
}
}
#ifdef notyet
static int
{
if (tuples == 0)
return (EINVAL);
for (i = 0; i < tuples; i++) {
if (addr_cells == 2)
else
if (size_cells == 2)
else
if (cur_size == 0)
return (EINVAL);
debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n",
}
return (0);
}
#endif
void
{
int reserved;
if (root < 0) {
return;
}
if (memory <= 0) {
/* Create proper '/memory' node. */
if (memory <= 0) {
"Could not fixup '/memory' "
"node, error code : %d!\n", memory);
return;
}
sizeof("memory"));
if (err < 0)
return;
}
NULL);
"Could not fixup '/memory' node : "
"%s %s property not found in root node!\n",
return;
}
/*
* Convert memreserve data to memreserve property
* Check if property already exists
*/
if (reserved &&
if (!buf)
return;
for (i = 0; i < reserved; i++) {
break;
if (rsize) {
/* Ensure endianess, and put cells into a buffer */
if (addr_cells == 2)
else
if (size_cells == 2)
else
}
}
/* Set property */
printf("Could not fixup 'memreserve' property.\n");
}
/* Count valid memory regions entries in sysinfo. */
for (i = 0; i < num; i++)
realmrno--;
if (realmrno == 0) {
"Could not fixup '/memory' node : "
"sysinfo doesn't contain valid memory regions info!\n");
return;
}
if (!buf)
return;
for (i = 0; i < num; i++) {
/* Ensure endianess, and put cells into a buffer */
if (addr_cells == 2)
else
if (size_cells == 2)
else
}
}
/* Set property */
}
void
{
char *ptr;
int serialno;
str--;
return;
if (no < 0)
return;
/* Serial number too long */
return;
if (sero < 0)
/*
* If serial device we're trying to assign
* stdout to doesn't exist in DT -- return.
*/
return;
}
}
/*
* Locate the blob, fix it up and return its location.
*/
static int
fdt_fixup(void)
{
len = 0;
debugf("fdt_fixup()\n");
return (0);
/* Create /chosen node (if not exists) */
/* Value assigned to fixup-applied does not matter. */
return (1);
return (1);
}
/*
* Copy DTB blob to specified location and return size
*/
int
{
int err;
err = fdt_setup_fdtp();
if (err) {
printf("No valid device tree blob found!\n");
return (0);
}
}
if (fdt_fixup() == 0)
return (0);
if (fdtp_va != 0) {
/* Overwrite the FDT with the fixed version. */
/* XXX Is this really appropriate? */
}
return (fdtp_size);
}
int
{
int flags;
char *cmd;
int i, err;
if (argc < 2) {
command_errmsg = "usage is 'fdt <command> [<args>]";
return (CMD_ERROR);
}
/*
* Validate fdt <command>.
*/
i = 0;
/* found it */
break;
}
i++;
}
command_errmsg = "unknown command";
return (CMD_ERROR);
}
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);
}
/*
* Call command handler.
*/
return (err);
}
static int
{
const char *addr;
char *cp;
fdt_to_load = NULL;
if (argc > 2)
else {
return (CMD_ERROR);
}
"Invalid address: %s", addr);
return (CMD_ERROR);
}
}
fdt_to_load = hdr;
return (CMD_OK);
}
static int
{
char *path;
int len, o;
if (path[0] == '/') {
if (len >= FDT_CWD_LEN)
goto fail;
} else {
/* Handle path specification relative to cwd */
if (len >= FDT_CWD_LEN)
goto fail;
}
if (o < 0) {
"could not find node: '%s'", path);
return (CMD_ERROR);
}
return (CMD_OK);
fail:
return (CMD_ERROR);
}
static int
{
int ver;
command_errmsg = "no device tree blob pointer?!";
return (CMD_ERROR);
}
pager_open();
if (ver >= 2) {
}
if (ver >= 3) {
}
if (ver >= 17) {
}
pager_close();
return (CMD_OK);
}
static int
{
const char *name;
char *path;
if (o < 0) {
"could not find node: '%s'", path);
return (CMD_ERROR);
}
for (depth = 0;
(o >= 0) && (depth >= 0);
if (depth > FDT_MAX_DEPTH) {
continue;
}
/* Skip root (i = 1) when printing devices */
for (i = 1; i <= depth; i++) {
break;
printf("/");
}
printf("\n");
}
return (CMD_OK);
}
static __inline int
isprint(int c)
{
return (c >= ' ' && c <= 0x7e);
}
static int
{
const char *d;
char ch;
int yesno, i;
if (len == 0)
return (0);
d = (const char *)data;
return (0);
*count = 0;
yesno = 1;
for (i = 0; i < len; i++) {
ch = *(d + i);
/* Count strings */
if (ch == '\0')
(*count)++;
continue;
}
yesno = 0;
break;
}
return (yesno);
}
static int
{
char *b, *tmp;
const char *d;
int buf_len, i, l;
/*
* Calculate the length for the string and allocate memory.
*
* Note that 'len' already includes at least one terminator.
*/
if (count > 1) {
/*
* Each token had already a terminator buried in 'len', but we
* only need one eventually, don't count space for these.
*/
/* Each consecutive token requires a ", " separator. */
}
/* Add some space for surrounding double quotes. */
/* Note that string being put in 'tmp' may be as big as 'buf_len'. */
if (b == NULL)
goto error;
free(b);
goto error;
}
b[0] = '\0';
/*
* Now that we have space, format the string.
*/
i = 0;
do {
d = (const char *)data + i;
l = strlen(d) + 1;
i += l;
} while (i < len);
*buf = b;
return (0);
return (1);
}
static int
{
char *b, *tmp;
const uint32_t *c;
int count, i, l;
/* Number of cells */
/*
* 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. */
}
/* Each cell will have a "0x" prefix */
l += count * 2;
/* Space for surrounding <> and terminator */
l += 3;
b = (char *)malloc(l);
if (b == NULL)
goto error;
free(b);
goto error;
}
b[0] = '\0';
strcat(b, "<");
for (i = 0; i < len; i += 4) {
}
strcat(b, ">");
*buf = b;
return (0);
return (1);
}
static int
{
char *b, *tmp;
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. */
/* Each byte will have a "0x" prefix */
l += len * 2;
/* Space for surrounding [] and terminator. */
l += 3;
b = (char *)malloc(l);
if (b == NULL)
goto error;
free(b);
goto error;
}
b[0] = '\0';
strcat(b, "[");
}
strcat(b, "]");
*buf = b;
return (0);
return (1);
}
static int
{
int count;
if (len == 0) {
return (1);
}
else if ((len % 4) == 0)
else
}
static int
{
const char *name;
const void *data;
return (1);
rv = 0;
if (len == 0) {
/* Property without value */
rv = 2;
goto out2;
}
goto out1;
}
/*
* Process property with value
*/
rv = 3;
goto out2;
}
rv = 4;
goto out2;
}
out1:
pager_open();
pager_close();
out2:
if (buf)
if (line)
return (rv);
}
static int
{
const char *buf;
const struct fdt_property *p;
if (p != NULL) {
if (mode == 1) {
/* Adding inexistant value in mode 1 is forbidden */
return (CMD_ERROR);
}
} else if (mode == 0) {
return (CMD_ERROR);
}
rv = 0;
switch (*buf) {
case '&':
/* phandles */
break;
case '<':
/* Data cells */
sizeof(uint32_t));
break;
case '[':
/* Data bytes */
sizeof(uint8_t));
break;
case '"':
default:
/* Default -- string */
break;
}
if (rv != 0) {
if (rv == -FDT_ERR_NOSPACE)
"Device tree blob is too small!\n");
else
}
return (rv);
}
/* Merge strings from argv into a single string */
static int
{
char *buf;
sz = 0;
/* Additional bytes for whitespaces between args */
"for string");
return (1);
}
idx = 0;
idx++;
}
return (0);
}
static int
{
int o;
} else {
*subpath = '\0';
}
return (1);
}
if (o < 0) {
"could not find node: '%s'", path);
return (1);
}
*nodeoff = o;
return (0);
}
static int
{
if (argc > 3) {
/* Merge property value strings into one */
return (CMD_ERROR);
} else
if (value) {
/* If value is specified -- try to modify prop. */
return (CMD_ERROR);
if (rv)
return (CMD_ERROR);
return (CMD_OK);
}
/* User wants to display properties */
if (o < 0) {
"could not find node: '%s'", path);
goto out;
}
depth = 0;
while (depth >= 0) {
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) {
"property");
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
{
int o;
if (argc > 3) {
/* Merge property value strings into one */
return (CMD_ERROR);
} else
return (CMD_ERROR);
return (CMD_ERROR);
return (CMD_OK);
}
static int
{
int o, rv;
if (argc > 2)
else {
return (CMD_ERROR);
}
if (o < 0) {
/* If node not found -- try to find & delete property */
return (CMD_ERROR);
"could not delete %s\n",
(rv == -FDT_ERR_NOTFOUND) ?
return (CMD_ERROR);
} else
return (CMD_OK);
}
/* If node exists -- remove node */
if (rv) {
return (CMD_ERROR);
}
return (CMD_OK);
}
static int
{
int o, rv;
if (argc > 2)
else {
return (CMD_ERROR);
}
return (CMD_ERROR);
if (rv < 0) {
if (rv == -FDT_ERR_NOSPACE)
"Device tree blob is too small!\n");
else
"Could not add node!\n");
return (CMD_ERROR);
}
return (CMD_OK);
}
static int
{
pager_open();
pager_close();
return (CMD_OK);
}
static int
{
int i, total;
pager_open();
if (total > 0) {
pager_output("Reserved memory regions:\n");
for (i = 0; i < total; i++) {
}
} else
pager_output("No reserved memory regions\n");
pager_close();
return (CMD_OK);
}
static int
{
printf("command not yet implemented\n");
return (CMD_ERROR);
}