binutils-gdb/sim/ppc/device_tree.c

507 lines
14 KiB
C

/* This file is part of the program psim.
Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DEVICE_TREE_C_
#define _DEVICE_TREE_C_
#ifndef STATIC_INLINE_DEVICE_TREE
#define STATIC_INLINE_DEVICE_TREE STATIC_INLINE
#endif
#include <string.h>
#include "basics.h"
#include "device_tree.h"
#include "devices.h"
#include "bfd.h"
enum { clayton_memory_size = 0x100000 };
/* insert the address into the device_nodes sorted list of addresses */
INLINE_DEVICE_TREE void
device_node_add_address(device_node *node,
unsigned_word lower_bound,
unsigned size,
device_access access,
void *init)
{
unsigned_word upper_bound = lower_bound + size;
device_address *new_address;
device_address **current_address;
/* find the insertion point */
current_address = &node->addresses;
while (*current_address != NULL
&& (*current_address)->upper_bound >= upper_bound) {
current_address = &(*current_address)->next_address;
}
/* insert */
new_address = ZALLOC(device_address);
new_address->lower_bound = lower_bound;
new_address->upper_bound = lower_bound + size;
new_address->size = size;
new_address->access = access;
new_address->init = init;
new_address->next_address = *current_address;
*current_address = new_address;
}
/* create a new device tree optionally making it a child of the parent
node */
INLINE_DEVICE_TREE device_node *
device_node_create(device_node *parent,
char *name,
device_type type,
device_callbacks *callbacks,
void *data)
{
device_node *new_node;
new_node = ZALLOC(device_node);
new_node->parent = parent;
new_node->name = name;
new_node->type = type;
new_node->callbacks = callbacks;
new_node->data = data;
if (parent != NULL) {
new_node->sibling = parent->children;
parent->children = new_node;
}
return new_node;
}
/* Binary file:
The specified file is a binary, assume VEA is required, construct a
fake device tree based on the addresses of the text / data segments
requested by the binary */
/* Update the fake device tree so that memory is allocated for this
section */
STATIC_INLINE_DEVICE_TREE void
update_memory_node_for_section(bfd *abfd,
asection *the_section,
PTR obj)
{
unsigned_word section_vma;
unsigned_word section_size;
device_access section_access;
void *section_init;
device_node *memory = (device_node*)obj;
/* skip the section if no memory to allocate */
if (! (bfd_get_section_flags(abfd, the_section) & SEC_ALLOC))
return;
/* check/ignore any sections of size zero */
section_size = bfd_get_section_size_before_reloc(the_section);
if (section_size == 0)
return;
/* find where it is to go */
section_vma = bfd_get_section_vma(abfd, the_section);
TRACE(trace_device_tree,
("name=%-7s, vma=0x%.8x, size=%6d, flags=%3x(%s%s%s%s )\n",
bfd_get_section_name(abfd, the_section),
section_vma, section_size,
bfd_get_section_flags(abfd, the_section),
bfd_get_section_flags(abfd, the_section) & SEC_LOAD ? " LOAD" : "",
bfd_get_section_flags(abfd, the_section) & SEC_CODE ? " CODE" : "",
bfd_get_section_flags(abfd, the_section) & SEC_DATA ? " DATA" : "",
bfd_get_section_flags(abfd, the_section) & SEC_ALLOC ? " ALLOC" : "",
bfd_get_section_flags(abfd, the_section) & SEC_READONLY ? " READONLY" : ""
));
if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) {
section_init = zalloc(section_size);
if (!bfd_get_section_contents(abfd,
the_section,
section_init, 0,
section_size)) {
bfd_perror("core:load_section()");
error("load of data failed");
return;
}
}
else {
section_init = NULL;
}
/* determine the devices access */
if (bfd_get_section_flags(abfd, the_section) & SEC_CODE)
section_access = (device_is_readable | device_is_executable);
else if (bfd_get_section_flags(abfd, the_section) & SEC_READONLY)
section_access = device_is_readable;
else
section_access = (device_is_readable | device_is_writeable);
/* find our memory and add this section to its list of addresses */
device_node_add_address(memory,
section_vma,
section_size,
section_access,
section_init);
}
/* construct the device tree from the executable */
STATIC_INLINE_DEVICE_TREE device_node *
create_option_device_node(device_node *root,
bfd *image)
{
device_node *option_node;
/* the option node and than its members */
option_node = device_node_create(root, "options", options_device,
NULL, NULL);
/* which endian are we ? */
device_node_create(option_node,
"little-endian?",
boolean_type_device,
NULL,
(void*)(image->xvec->byteorder_big_p ? 0 : -1));
/* what is the initial entry point */
device_node_create(option_node,
"program-counter",
integer_type_device,
NULL,
(void*)(bfd_get_start_address(image)));
/* address of top of boot stack */
TRACE(trace_tbd, ("create_optioin_device_node() - TBD - NT/OpenBoot?\n"));
device_node_create(option_node,
"stack-pointer",
integer_type_device,
NULL,
(void*)(bfd_get_start_address(image) == 0
? clayton_memory_size /* OEA */
: (image->xvec->flavour == bfd_target_elf_flavour
? 0xe0000000 /* elf */
: 0x20000000 /* xcoff */)));
/* execution environment */
device_node_create(option_node,
"vea?",
boolean_type_device,
NULL,
(void*)(bfd_get_start_address(image) == 0
? 0
: -1));
/* what type of binary */
TRACE(trace_tbd, ("create_optioin_device_node() - TBD - NT/OpenBoot?\n"));
device_node_create(option_node,
"elf?",
boolean_type_device,
NULL,
(void*)(image->xvec->flavour == bfd_target_elf_flavour
? -1 /* elf binary */
: 0 /* probably aix binary */));
/* must all memory transfers be naturally aligned? */
device_node_create(option_node,
"aligned?",
boolean_type_device,
NULL,
(void*)((WITH_ALIGNMENT == NONSTRICT_ALIGNMENT
|| image->xvec->byteorder_big_p
|| bfd_get_start_address(image) != 0)
? 0
: -1));
return option_node;
}
/* clatyon is a simple machine that does not require interrupts or any
thing else */
STATIC_INLINE_DEVICE_TREE device_node *
create_clayton_device_tree(bfd *image)
{
device_node *root;
device_node *io_node;
device_node *data_node;
device_node *memory_node;
/* the root */
root = ZALLOC(device_node);
/* memory - clayton has 2mb of RAM at location 0 */
memory_node = device_node_create(root,
"memory",
memory_device,
NULL,
NULL);
device_node_add_address(memory_node, 0x0, clayton_memory_size,
(device_is_readable
| device_is_writeable
| device_is_executable),
NULL);
/* io address space */
io_node = device_node_create(root, "io", bus_device, NULL, NULL);
/* and IO devices */
find_device_descriptor("console")
->creator(io_node, "console@0x400000,0");
find_device_descriptor("halt")
->creator(io_node, "halt@0x500000,0");
find_device_descriptor("icu")
->creator(io_node, "icu@0x600000,0");
/* data to load */
data_node = device_node_create(root, "image", data_device, NULL, NULL);
bfd_map_over_sections(image,
update_memory_node_for_section,
(PTR)data_node);
/* options */
create_option_device_node(root, image);
return root;
}
/* user mode executable build up a device tree that reflects this */
STATIC_INLINE_DEVICE_TREE device_node *
create_vea_device_tree(bfd *image)
{
device_node *root;
device_node *memory_node;
device_node *option_node;
/* the root */
root = ZALLOC(device_node);
/* memory */
memory_node = device_node_create(root, "memory", memory_device,
NULL, NULL);
bfd_map_over_sections(image,
update_memory_node_for_section,
(PTR)memory_node);
/* options - only endian so far */
option_node = create_option_device_node(root, image);
return root;
}
/* create a device tree from the specified file */
INLINE_DEVICE_TREE device_node *
device_tree_create(const char *file_name)
{
bfd *image;
device_node *tree;
bfd_init(); /* could be redundant but ... */
/* open the file */
image = bfd_openr(file_name, NULL);
if (image == NULL) {
bfd_perror("open failed:");
error("nothing loaded\n");
return NULL;
}
/* check it is valid */
if (!bfd_check_format(image, bfd_object)) {
printf_filtered("create_device_tree() - FIXME - should check more bfd bits\n");
printf_filtered("create_device_tree() - %s not an executable, assume device file\n", file_name);
bfd_close(image);
image = NULL;
}
/* depending on what was found about the file, load it */
if (image != NULL) {
if (bfd_get_start_address(image) == 0) {
TRACE(trace_device_tree, ("create_device_tree() - clayton image\n"));
tree = create_clayton_device_tree(image);
}
else if (bfd_get_start_address(image) > 0) {
TRACE(trace_device_tree, ("create_device_tree() - vea image\n"));
tree = create_vea_device_tree(image);
}
bfd_close(image);
}
else {
error("TBD - create_device_tree() text file defining device tree\n");
tree = NULL;
}
return tree;
}
/* traverse a device tree applying prefix/postfix functions to it */
INLINE_DEVICE_TREE void
device_tree_traverse(device_node *root,
device_tree_traverse_function *prefix,
device_tree_traverse_function *postfix,
void *data)
{
device_node *child;
if (prefix != NULL)
prefix(root, data);
for (child = root->children; child != NULL; child = child->sibling) {
device_tree_traverse(child, prefix, postfix, data);
}
if (postfix != NULL)
postfix(root, data);
}
/* query the device tree */
INLINE_DEVICE_TREE device_node *
device_tree_find_node(device_node *root,
const char *path)
{
char *chp;
int name_len;
device_node *child;
/* strip off any leading `/', `../' or `./' */
while (1) {
if (strncmp(path, "/", strlen("/")) == 0) {
while (root->parent != NULL)
root = root->parent;
path += strlen("/");
}
else if (strncmp(path, "./", strlen("./")) == 0) {
root = root;
path += strlen("./");
}
else if (strncmp(path, "../", strlen("../")) == 0) {
if (root->parent != NULL)
root = root->parent;
path += strlen("../");
}
else {
break;
}
}
/* find the qualified (with @) and unqualified names in the path */
chp = strchr(path, '/');
name_len = (chp == NULL
? strlen(path)
: chp - path);
/* search through children for a match */
for (child = root->children;
child != NULL;
child = child->sibling) {
if (strncmp(path, child->name, name_len) == 0
&& (strlen(child->name) == name_len
|| strchr(child->name, '@') == child->name + name_len)) {
if (path[name_len] == '\0')
return child;
else
return device_tree_find_node(child, path + name_len + 1);
}
}
return NULL;
}
INLINE_DEVICE_TREE device_node *device_tree_find_next_node
(device_node *root,
const char *path,
device_node *last);
INLINE_DEVICE_TREE signed_word
device_tree_find_int(device_node *root,
const char *path)
{
device_node *int_node = device_tree_find_node(root, path);
if (int_node == NULL) {
error("device_tree_find_int() - node %s does not exist\n", path);
return 0;
}
else if (int_node->type != integer_type_device) {
error("device_tree_find_int() - node %s is not an int\n", path);
return 0;
}
else {
return (signed_word)(int_node->data);
}
}
INLINE_DEVICE_TREE const char *device_tree_find_string
(device_node *root,
const char *path);
INLINE_DEVICE_TREE int
device_tree_find_boolean(device_node *root,
const char *path)
{
device_node *int_node = device_tree_find_node(root, path);
if (int_node == NULL) {
error("device_tree_find_boolean() - node %s does not exist\n", path);
return 0;
}
else if (int_node->type != boolean_type_device) {
error("device_tree_find_boolean() - node %s is not a boolean\n", path);
return 0;
}
else {
return (signed_word)(int_node->data);
}
}
INLINE_DEVICE_TREE void *device_tree_find_bytes
(device_node *root,
const char *path);
/* dump out a device node and addresses */
INLINE_DEVICE_TREE void
device_tree_dump(device_node *device,
void *ignore_data_argument)
{
printf_filtered("(device_node@0x%x\n", device);
printf_filtered(" (parent 0x%x)\n", device->parent);
printf_filtered(" (children 0x%x)\n", device->children);
printf_filtered(" (sibling 0x%x)\n", device->sibling);
printf_filtered(" (name %s)\n", device->name ? device->name : "(null)");
printf_filtered(" (type %d)\n", device->type);
printf_filtered(" (handlers 0x%x)\n", device->callbacks);
printf_filtered(" (addresses %d)\n", device->addresses);
printf_filtered(" (data %d)\n", device->data);
printf_filtered(")\n");
}
#endif /* _DEVICE_TREE_C_ */