mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
68ed285428
Since we require C11 now, we can assume many headers exist, and clean up all of the conditional includes. It's not like any of this code actually accounted for the headers not existing, just whether we could include them. The strings.h cleanup is a little nuanced: it isn't in C11, but every use of it in the codebase will include strings.h only if string.h doesn't exist. Since we now assume the C11 string.h exists, we'll never include strings.h, so we can delete it.
2028 lines
56 KiB
C
2028 lines
56 KiB
C
/* This file is part of the program psim.
|
|
|
|
Copyright 1994, 1995, 1996, 1997, 2003 Andrew Cagney
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
#ifndef _EMUL_CHIRP_C_
|
|
#define _EMUL_CHIRP_C_
|
|
|
|
/* Note: this module is called via a table. There is no benefit in
|
|
making it inline */
|
|
|
|
#include "emul_generic.h"
|
|
#include "emul_chirp.h"
|
|
|
|
#include <string.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifndef STATIC_INLINE_EMUL_CHIRP
|
|
#define STATIC_INLINE_EMUL_CHIRP STATIC_INLINE
|
|
#endif
|
|
|
|
|
|
/* EMULATION
|
|
|
|
|
|
OpenFirmware - IEEE Standard for Boot (Initialization
|
|
Configuration) Firmware.
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
|
|
BUGS
|
|
|
|
|
|
This code assumes that the memory node has #address-cells and
|
|
#size-cells set to one. For future implementations, this may not
|
|
be the case.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Descriptor of the open boot services being emulated */
|
|
|
|
typedef int (chirp_handler)
|
|
(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia);
|
|
|
|
typedef struct _chirp_services {
|
|
const char *name;
|
|
chirp_handler *handler;
|
|
} chirp_services;
|
|
|
|
|
|
/* The OpenBoot emulation is, at any time either waiting for a client
|
|
request or waiting on a client callback */
|
|
typedef enum {
|
|
serving,
|
|
emulating,
|
|
faulting,
|
|
} chirp_emul_state;
|
|
|
|
struct _os_emul_data {
|
|
chirp_emul_state state;
|
|
unsigned_word return_address;
|
|
unsigned_word arguments;
|
|
unsigned_word n_args;
|
|
unsigned_word n_returns;
|
|
chirp_services *service;
|
|
device *root;
|
|
chirp_services *services;
|
|
/* configuration */
|
|
unsigned_word memory_size;
|
|
unsigned_word real_base;
|
|
unsigned_word real_size;
|
|
unsigned_word virt_base;
|
|
unsigned_word virt_size;
|
|
int real_mode;
|
|
int little_endian;
|
|
int floating_point_available;
|
|
int interrupt_prefix;
|
|
unsigned_word load_base;
|
|
/* hash table */
|
|
unsigned_word nr_page_table_entry_groups;
|
|
unsigned_word htab_offset;
|
|
unsigned_word htab_ra;
|
|
unsigned_word htab_va;
|
|
unsigned_word sizeof_htab;
|
|
/* virtual address of htab */
|
|
unsigned_word stack_offset;
|
|
unsigned_word stack_ra;
|
|
unsigned_word stack_va;
|
|
unsigned_word sizeof_stack;
|
|
/* addresses of emulation instructions virtual/real */
|
|
unsigned_word code_offset;
|
|
unsigned_word code_va;
|
|
unsigned_word code_ra;
|
|
unsigned_word sizeof_code;
|
|
unsigned_word code_client_va;
|
|
unsigned_word code_client_ra;
|
|
unsigned_word code_callback_va;
|
|
unsigned_word code_callback_ra;
|
|
unsigned_word code_loop_va;
|
|
unsigned_word code_loop_ra;
|
|
};
|
|
|
|
|
|
/* returns the name of the corresponding Ihandle */
|
|
static const char *
|
|
ihandle_name(device_instance *ihandle)
|
|
{
|
|
if (ihandle == NULL)
|
|
return "";
|
|
else
|
|
return device_name(device_instance_device(ihandle));
|
|
}
|
|
|
|
|
|
|
|
/* Read/write the argument list making certain that all values are
|
|
converted to/from host byte order.
|
|
|
|
In the below only n_args+n_returns is read/written */
|
|
|
|
static int
|
|
chirp_read_t2h_args(void *args,
|
|
int sizeof_args,
|
|
int n_args,
|
|
int n_returns,
|
|
os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
unsigned_cell *words;
|
|
int i;
|
|
/* check against the number of arguments specified by the client
|
|
program */
|
|
if ((n_args >= 0 && data->n_args != n_args)
|
|
|| (n_returns >= 0 && data->n_returns != n_returns)) {
|
|
TRACE(trace_os_emul, ("%s - invalid nr of args - n_args=%ld, n_returns=%ld\n",
|
|
data->service->name,
|
|
(long)data->n_args,
|
|
(long)data->n_returns));
|
|
return -1;
|
|
}
|
|
/* check that there is enough space */
|
|
if (sizeof(unsigned_cell) * (data->n_args + data->n_returns) > sizeof_args)
|
|
return -1;
|
|
/* bring in the data */
|
|
memset(args, 0, sizeof_args);
|
|
emul_read_buffer(args, data->arguments + 3 * sizeof(unsigned_cell),
|
|
sizeof(unsigned_cell) * (data->n_args + data->n_returns),
|
|
processor, cia);
|
|
/* convert all words to host format */
|
|
words = args;
|
|
for (i = 0; i < (sizeof_args / sizeof(unsigned_cell)); i++)
|
|
words[i] = T2H_cell(words[i]);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
chirp_write_h2t_args(void *args,
|
|
int sizeof_args,
|
|
os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
int i;
|
|
unsigned_cell *words;
|
|
/* convert to target everything */
|
|
words = args;
|
|
for (i = 0; i < (sizeof_args / sizeof(unsigned_cell)); i++)
|
|
words[i] = H2T_cell(words[i]);
|
|
/* bring in the data */
|
|
emul_write_buffer(args, data->arguments + 3 * sizeof(unsigned_cell),
|
|
sizeof(unsigned_cell) * (data->n_args + data->n_returns),
|
|
processor, cia);
|
|
}
|
|
|
|
|
|
/* OpenBoot emulation functions */
|
|
|
|
/* client interface */
|
|
|
|
static int
|
|
chirp_emul_test(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct test_args {
|
|
/*in*/
|
|
unsigned_cell name; /*string*/
|
|
/*out*/
|
|
unsigned_cell missing;
|
|
} args;
|
|
char name[32];
|
|
chirp_services *service = NULL;
|
|
/* read in the arguments */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(name, args.name, sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("test - in - name=`%s'\n", name));
|
|
/* see if we know about the service */
|
|
service = data->services;
|
|
while (service->name != NULL && strcmp(service->name, name) != 0) {
|
|
service++;
|
|
}
|
|
if (service->name == NULL)
|
|
args.missing = -1;
|
|
else
|
|
args.missing = 0;
|
|
/* write the arguments back out */
|
|
TRACE(trace_os_emul, ("test - out - missing=%ld\n",
|
|
(long)args.missing));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Device tree */
|
|
|
|
static int
|
|
chirp_emul_peer(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct peer_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
/*out*/
|
|
unsigned_cell sibling_phandle;
|
|
} args;
|
|
device *phandle;
|
|
device *sibling_phandle = NULL;
|
|
/* read in the arguments */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
TRACE(trace_os_emul, ("peer - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle))));
|
|
/* find the peer */
|
|
if (args.phandle == 0) {
|
|
sibling_phandle = data->root;
|
|
args.sibling_phandle = device_to_external(sibling_phandle);
|
|
}
|
|
else if (phandle == NULL) {
|
|
sibling_phandle = NULL;
|
|
args.sibling_phandle = -1;
|
|
}
|
|
else {
|
|
sibling_phandle = device_sibling(phandle);
|
|
if (sibling_phandle == NULL)
|
|
args.sibling_phandle = 0;
|
|
else
|
|
args.sibling_phandle = device_to_external(sibling_phandle);
|
|
}
|
|
/* write the arguments back out */
|
|
TRACE(trace_os_emul, ("peer - out - sibling_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.sibling_phandle,
|
|
(unsigned long)sibling_phandle,
|
|
(sibling_phandle == NULL ? "" : device_name(sibling_phandle))));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_child(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct child_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
/*out*/
|
|
unsigned_cell child_phandle;
|
|
} args;
|
|
device *phandle;
|
|
device *child_phandle;
|
|
/* read the arguments in */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
TRACE(trace_os_emul, ("child - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle))));
|
|
/* find a child */
|
|
if (args.phandle == 0
|
|
|| phandle == NULL) {
|
|
child_phandle = NULL;
|
|
args.child_phandle = -1;
|
|
}
|
|
else {
|
|
child_phandle = device_child(phandle);
|
|
if (child_phandle == NULL)
|
|
args.child_phandle = 0;
|
|
else
|
|
args.child_phandle = device_to_external(child_phandle);
|
|
}
|
|
/* write the result out */
|
|
TRACE(trace_os_emul, ("child - out - child_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.child_phandle,
|
|
(unsigned long)child_phandle,
|
|
(child_phandle == NULL ? "" : device_name(child_phandle))));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_parent(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct parent_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
/*out*/
|
|
unsigned_cell parent_phandle;
|
|
} args;
|
|
device *phandle;
|
|
device *parent_phandle;
|
|
/* read the args in */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
TRACE(trace_os_emul, ("parent - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle))));
|
|
/* find a parent */
|
|
if (args.phandle == 0
|
|
|| phandle == NULL) {
|
|
parent_phandle = NULL;
|
|
args.parent_phandle = -1;
|
|
}
|
|
else {
|
|
parent_phandle = device_parent(phandle);
|
|
if (parent_phandle == NULL)
|
|
args.parent_phandle = 0;
|
|
else
|
|
args.parent_phandle = device_to_external(parent_phandle);
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("parent - out - parent_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.parent_phandle,
|
|
(unsigned long)parent_phandle,
|
|
(parent_phandle == NULL ? "" : device_name(parent_phandle))));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_instance_to_package(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct instance_to_package_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
/*out*/
|
|
unsigned_cell phandle;
|
|
} args;
|
|
device_instance *ihandle;
|
|
device *phandle = NULL;
|
|
/* read the args in */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("instance-to-package - in - ihandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle)));
|
|
/* find the corresponding phandle */
|
|
if (ihandle == NULL) {
|
|
phandle = NULL;
|
|
args.phandle = -1;
|
|
}
|
|
else {
|
|
phandle = device_instance_device(ihandle);
|
|
args.phandle = device_to_external(phandle);
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("instance-to-package - out - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle))));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_getproplen(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct getproplen_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
unsigned_cell name;
|
|
/*out*/
|
|
unsigned_cell proplen;
|
|
} args;
|
|
char name[32];
|
|
device *phandle;
|
|
/* read the args in */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 2, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
emul_read_string(name,
|
|
args.name,
|
|
sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("getproplen - in - phandle=0x%lx(0x%lx`%s') name=`%s'\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle)),
|
|
name));
|
|
/* find our prop and get its length */
|
|
if (args.phandle == 0
|
|
|| phandle == NULL) {
|
|
args.proplen = -1;
|
|
}
|
|
else {
|
|
const device_property *prop = device_find_property(phandle, name);
|
|
if (prop == (device_property*)0) {
|
|
args.proplen = -1;
|
|
}
|
|
else {
|
|
args.proplen = prop->sizeof_array;
|
|
}
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("getproplen - out - proplen=%ld\n",
|
|
(unsigned long)args.proplen));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_getprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct getprop_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
unsigned_cell name;
|
|
unsigned_cell buf;
|
|
unsigned_cell buflen;
|
|
/*out*/
|
|
unsigned_cell size;
|
|
} args;
|
|
char name[32];
|
|
device *phandle;
|
|
/* read in the args, the return is optional */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 4, -1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
emul_read_string(name,
|
|
args.name,
|
|
sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("getprop - in - phandle=0x%lx(0x%lx`%s') name=`%s' buf=0x%lx buflen=%ld\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle)),
|
|
name,
|
|
(unsigned long)args.buf,
|
|
(unsigned long)args.buflen));
|
|
/* get the property */
|
|
if (args.phandle == 0
|
|
|| phandle == NULL) {
|
|
args.size = -1;
|
|
}
|
|
else {
|
|
const device_property *prop = device_find_property(phandle, name);
|
|
if (prop == NULL) {
|
|
args.size = -1;
|
|
}
|
|
else {
|
|
int size = args.buflen;
|
|
if (size > prop->sizeof_array)
|
|
size = prop->sizeof_array;
|
|
emul_write_buffer(prop->array, args.buf,
|
|
size,
|
|
processor, cia);
|
|
args.size = size;
|
|
switch (prop->type) {
|
|
case string_property:
|
|
TRACE(trace_os_emul, ("getprop - string `%s'\n",
|
|
device_find_string_property(phandle, name)));
|
|
break;
|
|
case ihandle_property:
|
|
TRACE(trace_os_emul, ("getprop - ihandle=0x%lx(0x%lx`%s')\n",
|
|
BE2H_cell(*(unsigned_cell*)prop->array),
|
|
(unsigned long)device_find_ihandle_property(phandle, name),
|
|
ihandle_name(device_find_ihandle_property(phandle, name))));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* write back the result */
|
|
if (data->n_returns == 0)
|
|
TRACE(trace_os_emul, ("getprop - out - size=%ld (not returned)\n",
|
|
(unsigned long)args.size));
|
|
else {
|
|
TRACE(trace_os_emul, ("getprop - out - size=%ld\n",
|
|
(unsigned long)args.size));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_nextprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct nextprop_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
unsigned_cell previous;
|
|
unsigned_cell buf;
|
|
/*out*/
|
|
unsigned_cell flag;
|
|
} args;
|
|
char previous[32];
|
|
device *phandle;
|
|
/* read in the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
if (args.previous != 0)
|
|
emul_read_string(previous,
|
|
args.previous,
|
|
sizeof(previous),
|
|
processor, cia);
|
|
else
|
|
/* If previous is NULL, make it look like the empty string. The
|
|
next property after the empty string is the first property. */
|
|
strcpy (previous, "");
|
|
TRACE(trace_os_emul, ("nextprop - in - phandle=0x%lx(0x%lx`%s') previous=`%s' buf=0x%lx\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle)),
|
|
previous,
|
|
(unsigned long)args.buf));
|
|
/* find the next property */
|
|
if (args.phandle == 0
|
|
|| phandle == NULL) {
|
|
args.flag = -1;
|
|
}
|
|
else {
|
|
const device_property *prev_prop = device_find_property(phandle, previous);
|
|
if (prev_prop == NULL) {
|
|
if (strcmp (previous, "") == 0)
|
|
args.flag = 0; /* No properties */
|
|
else
|
|
args.flag = -1; /* name invalid */
|
|
}
|
|
else {
|
|
const device_property *next_prop;
|
|
if (strcmp (previous, "") == 0) {
|
|
next_prop = prev_prop; /* The first property. */
|
|
}
|
|
else {
|
|
next_prop = device_next_property(prev_prop);
|
|
}
|
|
if (next_prop == NULL) {
|
|
args.flag = 0; /* last property */
|
|
}
|
|
else {
|
|
emul_write_buffer(next_prop->name, args.buf, strlen(next_prop->name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("nextprop - name=`%s'\n", next_prop->name));
|
|
args.flag = 1; /* worked ok */
|
|
}
|
|
}
|
|
}
|
|
/* write back the result */
|
|
TRACE(trace_os_emul, ("nextprop - out - flag=%ld\n",
|
|
(unsigned long)args.flag));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
chirp_emul_setprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: setprop method not implemented\n");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
chirp_emul_canon(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct canon_args {
|
|
/*in*/
|
|
unsigned_cell device_specifier;
|
|
unsigned_cell buf;
|
|
unsigned_cell buflen;
|
|
/*out*/
|
|
unsigned_cell length;
|
|
} args;
|
|
char device_specifier[1024];
|
|
device *phandle;
|
|
const char *path;
|
|
int length;
|
|
/* read in the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(device_specifier,
|
|
args.device_specifier,
|
|
sizeof(device_specifier),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("canon - in - device_specifier=`%s' buf=0x%lx buflen=%lx\n",
|
|
device_specifier,
|
|
(unsigned long)args.buf,
|
|
(unsigned long)args.buflen));
|
|
/* canon the name */
|
|
phandle = tree_find_device(data->root, device_specifier);
|
|
if (phandle == NULL) {
|
|
length = -1;
|
|
path = "";
|
|
args.length = -1;
|
|
}
|
|
else {
|
|
path = device_path(phandle);
|
|
length = strlen(path);
|
|
if (length >= args.buflen)
|
|
length = args.buflen - 1;
|
|
emul_write_buffer(path, args.buf, length,
|
|
processor, cia);
|
|
args.length = length;
|
|
}
|
|
/* write back the result */
|
|
TRACE(trace_os_emul, ("canon - out - length=%ld buf=`%s'\n",
|
|
(unsigned long)args.length,
|
|
path));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_finddevice(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct finddevice_args {
|
|
/*in*/
|
|
unsigned_cell device_specifier;
|
|
/*out*/
|
|
unsigned_cell phandle;
|
|
} args;
|
|
char device_specifier[1024];
|
|
device *phandle;
|
|
/* get the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(device_specifier,
|
|
args.device_specifier,
|
|
sizeof(device_specifier),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("finddevice - in - device_specifier=`%s'\n",
|
|
device_specifier));
|
|
/* find the device */
|
|
phandle = tree_find_device(data->root, device_specifier);
|
|
if (phandle == NULL)
|
|
args.phandle = -1;
|
|
else
|
|
args.phandle = device_to_external(phandle);
|
|
/* return its phandle */
|
|
TRACE(trace_os_emul, ("finddevice - out - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle))));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_instance_to_path(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct instance_to_path_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
unsigned_cell buf;
|
|
unsigned_cell buflen;
|
|
/*out*/
|
|
unsigned_cell length;
|
|
} args;
|
|
device_instance *ihandle;
|
|
const char *path;
|
|
int length;
|
|
/* get the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("instance-to-path - in - ihandle=0x%lx(0x%lx`%s') buf=0x%lx buflen=%ld\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle),
|
|
(unsigned long)args.buf,
|
|
(unsigned long)args.buflen));
|
|
/* get the devices name */
|
|
if (ihandle == NULL) {
|
|
args.length = -1;
|
|
path = "(null)";
|
|
}
|
|
else {
|
|
path = device_instance_path(ihandle);
|
|
length = strlen(path);
|
|
if (length >= args.buflen)
|
|
length = args.buflen - 1;
|
|
emul_write_buffer(path, args.buf, length,
|
|
processor, cia);
|
|
args.length = length;
|
|
}
|
|
/* return its phandle */
|
|
TRACE(trace_os_emul, ("instance-to-path - out - length=%ld buf=`%s')\n",
|
|
(unsigned long)args.length,
|
|
path));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_package_to_path(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct package_to_path_args {
|
|
/*in*/
|
|
unsigned_cell phandle;
|
|
unsigned_cell buf;
|
|
unsigned_cell buflen;
|
|
/*out*/
|
|
unsigned_cell length;
|
|
} args;
|
|
device *phandle;
|
|
const char *path;
|
|
/* get the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
phandle = external_to_device(data->root, args.phandle);
|
|
TRACE(trace_os_emul, ("package-to-path - in - phandle=0x%lx(0x%lx`%s') buf=0x%lx buflen=%ld\n",
|
|
(unsigned long)args.phandle,
|
|
(unsigned long)phandle,
|
|
(phandle == NULL ? "" : device_name(phandle)),
|
|
(unsigned long)args.buf,
|
|
(unsigned long)args.buflen));
|
|
/* get the devices name */
|
|
if (phandle == NULL) {
|
|
args.length = -1;
|
|
path = "(null)";
|
|
}
|
|
else {
|
|
int length;
|
|
path = device_path(phandle);
|
|
length = strlen(path);
|
|
if (length >= args.buflen)
|
|
length = args.buflen - 1;
|
|
emul_write_buffer(path, args.buf, length,
|
|
processor, cia);
|
|
args.length = length;
|
|
}
|
|
/* return its phandle */
|
|
TRACE(trace_os_emul, ("package-to-path - out - length=%ld buf=`%s')\n",
|
|
(unsigned long)args.length,
|
|
path));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_call_method(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct call_method_args {
|
|
/*in*/
|
|
unsigned_cell method;
|
|
unsigned_cell ihandle;
|
|
/*in/out*/
|
|
unsigned_cell stack[13]; /*6in + 6out + catch */
|
|
} args;
|
|
char method[32];
|
|
device_instance *ihandle;
|
|
/* some useful info about our mini stack */
|
|
int n_stack_args;
|
|
int n_stack_returns;
|
|
int stack_catch_result;
|
|
int stack_returns;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), -1, -1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(method,
|
|
args.method,
|
|
sizeof(method),
|
|
processor, cia);
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
n_stack_args = data->n_args - 2;
|
|
n_stack_returns = data->n_returns - 1;
|
|
stack_catch_result = n_stack_args;
|
|
stack_returns = stack_catch_result + 1;
|
|
TRACE(trace_os_emul, ("call-method - in - n_args=%ld n_returns=%ld method=`%s' ihandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)data->n_args,
|
|
(unsigned long)data->n_returns,
|
|
method,
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle)));
|
|
/* see if we can emulate this method */
|
|
if (ihandle == NULL) {
|
|
/* OpenFirmware doesn't define this error */
|
|
error("chirp: invalid ihandle passed to call-method method");
|
|
}
|
|
else {
|
|
args.stack[stack_catch_result] =
|
|
device_instance_call_method(ihandle,
|
|
method,
|
|
n_stack_args,
|
|
&args.stack[0],
|
|
n_stack_returns,
|
|
&args.stack[stack_returns]);
|
|
}
|
|
/* finished */
|
|
TRACE(trace_os_emul, ("call-method - out - catch-result=%ld\n",
|
|
(unsigned long)args.stack[stack_catch_result]));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Device I/O */
|
|
|
|
static int
|
|
chirp_emul_open(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct open_args {
|
|
/*in*/
|
|
unsigned_cell device_specifier;
|
|
/*out*/
|
|
unsigned_cell ihandle;
|
|
} args;
|
|
char device_specifier[1024];
|
|
device_instance *ihandle;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(device_specifier,
|
|
args.device_specifier,
|
|
sizeof(device_specifier),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("open - in - device_specifier=`%s'\n",
|
|
device_specifier));
|
|
/* open the device */
|
|
ihandle = tree_instance(data->root, device_specifier);
|
|
if (ihandle == NULL)
|
|
args.ihandle = -1;
|
|
else
|
|
args.ihandle = device_instance_to_external(ihandle);
|
|
/* return the ihandle result */
|
|
TRACE(trace_os_emul, ("open - out - ihandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle)));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_close(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct close_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
/*out*/
|
|
} args;
|
|
device_instance *ihandle;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 0, data, processor, cia))
|
|
return -1;
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("close - in - ihandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle)));
|
|
/* close the device */
|
|
if (ihandle == NULL) {
|
|
/* OpenFirmware doesn't define this error */
|
|
error("chirp: invalid ihandle passed to close method");
|
|
}
|
|
else {
|
|
device_instance_delete(ihandle);
|
|
}
|
|
/* return the ihandle result */
|
|
TRACE(trace_os_emul, ("close - out\n"));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_read(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct read_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
unsigned_cell addr;
|
|
unsigned_cell len;
|
|
/*out*/
|
|
unsigned_cell actual;
|
|
} args;
|
|
char buf[1024];
|
|
device_instance *ihandle;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("read - in - ihandle=0x%lx(0x%lx`%s') addr=0x%lx len=%ld\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle),
|
|
(unsigned long)args.addr,
|
|
(unsigned long)args.len));
|
|
if (ihandle == NULL) {
|
|
/* OpenFirmware doesn't define this error */
|
|
error("chirp: invalid ihandle passed to read method");
|
|
}
|
|
else {
|
|
/* do the reads */
|
|
int actual = 0;
|
|
while (actual < args.len) {
|
|
int remaining = args.len - actual;
|
|
int to_read = (remaining <= sizeof(buf) ? remaining : sizeof(buf));
|
|
int nr_read = device_instance_read(ihandle, buf, to_read);
|
|
if (nr_read < 0) {
|
|
actual = nr_read; /* the error */
|
|
break;
|
|
}
|
|
else if (nr_read == 0) {
|
|
break;
|
|
}
|
|
emul_write_buffer(buf,
|
|
args.addr + actual,
|
|
nr_read,
|
|
processor, cia);
|
|
actual += nr_read;
|
|
}
|
|
if (actual >= 0) {
|
|
args.actual = actual;
|
|
if (actual < sizeof(buf))
|
|
buf[actual] = '\0';
|
|
else
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
}
|
|
else {
|
|
switch (actual) {
|
|
case sim_io_eof:
|
|
args.actual = 0;
|
|
break;
|
|
case sim_io_not_ready:
|
|
ASSERT(sim_io_not_ready == -2);
|
|
args.actual = sim_io_not_ready;
|
|
break;
|
|
default:
|
|
error("Bad error value %ld", (long)actual);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("read - out - actual=%ld `%s'\n",
|
|
(long)args.actual,
|
|
((args.actual > 0 && args.actual < sizeof(buf)) ? buf : "")
|
|
));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_write(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct write_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
unsigned_cell addr;
|
|
unsigned_cell len;
|
|
/*out*/
|
|
unsigned_cell actual;
|
|
} args;
|
|
char buf[1024];
|
|
device_instance *ihandle;
|
|
int actual;
|
|
/* get the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
actual = args.len;
|
|
if (actual >= sizeof(buf))
|
|
actual = sizeof(buf) - 1;
|
|
emul_read_buffer(buf,
|
|
args.addr,
|
|
actual,
|
|
processor, cia);
|
|
buf[actual] = '\0';
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("write - in - ihandle=0x%lx(0x%lx`%s') `%s' (%ld)\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle),
|
|
buf, (long)actual));
|
|
if (ihandle == NULL) {
|
|
/* OpenFirmware doesn't define this error */
|
|
error("chirp: invalid ihandle passed to write method");
|
|
}
|
|
else {
|
|
/* write it out */
|
|
actual = device_instance_write(ihandle, buf, actual);
|
|
if (actual < 0)
|
|
args.actual = 0;
|
|
else
|
|
args.actual = actual;
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("write - out - actual=%ld\n",
|
|
(long)args.actual));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_seek(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct seek_args {
|
|
/*in*/
|
|
unsigned_cell ihandle;
|
|
unsigned_cell pos_hi;
|
|
unsigned_cell pos_lo;
|
|
/*out*/
|
|
unsigned_cell status;
|
|
} args;
|
|
int status;
|
|
device_instance *ihandle;
|
|
/* get the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
ihandle = external_to_device_instance(data->root, args.ihandle);
|
|
TRACE(trace_os_emul, ("seek - in - ihandle=0x%lx(0x%lx`%s') pos.hi=0x%lx pos.lo=0x%lx\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)ihandle,
|
|
ihandle_name(ihandle),
|
|
args.pos_hi, args.pos_lo));
|
|
if (ihandle == NULL) {
|
|
/* OpenFirmware doesn't define this error */
|
|
error("chirp: invalid ihandle passed to seek method");
|
|
}
|
|
else {
|
|
/* seek it out */
|
|
status = device_instance_seek(ihandle, args.pos_hi, args.pos_lo);
|
|
args.status = status;
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("seek - out - status=%ld\n",
|
|
(long)args.status));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* memory */
|
|
|
|
static int
|
|
chirp_emul_claim(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
/* NOTE: the client interface claim routine is *very* different to
|
|
the "claim" method described in IEEE-1275 appendix A. The latter
|
|
uses real addresses while this uses virtual (effective)
|
|
addresses. */
|
|
struct claim_args {
|
|
/* in */
|
|
unsigned_cell virt;
|
|
unsigned_cell size;
|
|
unsigned_cell align;
|
|
/* out */
|
|
unsigned_cell baseaddr;
|
|
} args;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args),
|
|
3 /*n_args*/, 1 /*n_returns*/,
|
|
data, processor, cia))
|
|
return -1;
|
|
TRACE(trace_os_emul, ("claim - in - virt=0x%lx size=%ld align=%d\n",
|
|
(unsigned long)args.virt,
|
|
(long int)args.size,
|
|
(int)args.align));
|
|
/* use the memory device to allocate (real) memory at the requested
|
|
address */
|
|
{
|
|
device_instance *memory = tree_find_ihandle_property(data->root, "/chosen/memory");
|
|
unsigned_cell mem_in[3];
|
|
unsigned_cell mem_out[1];
|
|
mem_in[0] = args.align; /*top-of-stack*/
|
|
mem_in[1] = args.size;
|
|
mem_in[2] = args.virt;
|
|
if (device_instance_call_method(memory, "claim",
|
|
3, mem_in, 1, mem_out) < 0)
|
|
error("chirp: claim failed to allocate memory virt=0x%lx size=%ld align=%d",
|
|
(unsigned long)args.virt,
|
|
(long int)args.size,
|
|
(int)args.align);
|
|
args.baseaddr = mem_out[0];
|
|
}
|
|
/* if using virtual addresses, create a 1-1 map of this address space */
|
|
if (!data->real_mode) {
|
|
error("chirp: claim method does not support virtual mode");
|
|
}
|
|
/* return the base address */
|
|
TRACE(trace_os_emul, ("claim - out - baseaddr=0x%lx\n",
|
|
(unsigned long)args.baseaddr));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_release(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
/* NOTE: the client interface release routine is *very* different to
|
|
the "claim" method described in IEEE-1275 appendix A. The latter
|
|
uses real addresses while this uses virtual (effective)
|
|
addresses. */
|
|
struct claim_args {
|
|
/* in */
|
|
unsigned_cell virt;
|
|
unsigned_cell size;
|
|
/* out */
|
|
} args;
|
|
/* read the args */
|
|
if (chirp_read_t2h_args(&args, sizeof(args),
|
|
2 /*n_args*/, 0 /*n_returns*/,
|
|
data, processor, cia))
|
|
return -1;
|
|
TRACE(trace_os_emul, ("release - in - virt=0x%lx size=%ld\n",
|
|
(unsigned long)args.virt,
|
|
(long int)args.size));
|
|
/* use the memory device to release (real) memory at the requested
|
|
address */
|
|
{
|
|
device_instance *memory = tree_find_ihandle_property(data->root, "/chosen/memory");
|
|
unsigned_cell mem_in[2];
|
|
mem_in[0] = args.size;
|
|
mem_in[1] = args.virt;
|
|
if (device_instance_call_method(memory, "release",
|
|
2, mem_in, 0, NULL) < 0)
|
|
error("chirp: claim failed to release memory virt=0x%lx size=%ld",
|
|
(unsigned long)args.virt,
|
|
(long int)args.size);
|
|
}
|
|
/* if using virtual addresses, remove the 1-1 map of this address space */
|
|
if (!data->real_mode) {
|
|
error("chirp: release method does not support virtual mode");
|
|
}
|
|
/* return the base address */
|
|
TRACE(trace_os_emul, ("release - out\n"));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Control transfer */
|
|
|
|
static int
|
|
chirp_emul_boot(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
/* unlike OpenFirmware this one can take an argument */
|
|
struct boot_args {
|
|
/*in*/
|
|
unsigned_cell bootspec;
|
|
/*out*/
|
|
} args;
|
|
char bootspec[1024];
|
|
/* read in the arguments */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), -1, 0, data, processor, cia))
|
|
cpu_halt(processor, cia, was_exited, -1);
|
|
if (args.bootspec != 0)
|
|
emul_read_string(bootspec, args.bootspec, sizeof(bootspec),
|
|
processor, cia);
|
|
else
|
|
strcpy(bootspec, "(null)");
|
|
TRACE(trace_os_emul, ("boot - in bootspec=`%s'\n", bootspec));
|
|
/* just report this and exit */
|
|
printf_filtered("chrp: boot %s called, exiting.\n", bootspec);
|
|
cpu_halt(processor, cia, was_exited, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_enter(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: enter method not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_exit(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
/* unlike OpenBoot this one can take an argument */
|
|
struct exit_args {
|
|
/*in*/
|
|
signed_cell status;
|
|
/*out*/
|
|
} args;
|
|
if (chirp_read_t2h_args(&args, sizeof(args), -1, 0, data, processor, cia))
|
|
cpu_halt(processor, cia, was_exited, -1);
|
|
cpu_halt(processor, cia, was_exited, args.status);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_chain(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: chain method not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* user interface */
|
|
|
|
static int
|
|
chirp_emul_interpret(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: interpret method not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_set_callback(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: set_callback method not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_set_symbol_lookup(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: set_symbol_lookup method not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Time */
|
|
|
|
static int
|
|
chirp_emul_milliseconds(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct test_args {
|
|
/*in*/
|
|
/*out*/
|
|
unsigned_cell ms;
|
|
} args;
|
|
unsigned64 time;
|
|
/* read in the arguments */
|
|
if (chirp_read_t2h_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
/* make up a number */
|
|
time = event_queue_time(psim_event_queue(cpu_system(processor))) / 1000000;
|
|
args.ms = time;
|
|
/* write the arguments back out */
|
|
TRACE(trace_os_emul, ("milliseconds - out - ms=%ld\n",
|
|
(unsigned long)args.ms));
|
|
chirp_write_h2t_args(&args,
|
|
sizeof(args),
|
|
data,
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
static chirp_services services[] = {
|
|
|
|
/* client interface */
|
|
{ "test", chirp_emul_test },
|
|
|
|
/* device tree */
|
|
{ "peer", chirp_emul_peer },
|
|
{ "child", chirp_emul_child },
|
|
{ "parent", chirp_emul_parent },
|
|
{ "instance-to-package", chirp_emul_instance_to_package },
|
|
{ "getproplen", chirp_emul_getproplen },
|
|
{ "getprop", chirp_emul_getprop },
|
|
{ "nextprop", chirp_emul_nextprop },
|
|
/* { "setprop", chirp_emul_setprop }, */
|
|
{ "canon", chirp_emul_canon },
|
|
{ "finddevice", chirp_emul_finddevice },
|
|
{ "instance-to-path", chirp_emul_instance_to_path },
|
|
{ "package-to-path", chirp_emul_package_to_path },
|
|
{ "call-method", chirp_emul_call_method },
|
|
|
|
/* device I/O */
|
|
{ "open", chirp_emul_open },
|
|
{ "close", chirp_emul_close },
|
|
{ "read", chirp_emul_read },
|
|
{ "write", chirp_emul_write },
|
|
{ "seek", chirp_emul_seek },
|
|
{ "write", chirp_emul_write },
|
|
|
|
/* memory */
|
|
{ "claim", chirp_emul_claim },
|
|
{ "release", chirp_emul_release },
|
|
|
|
/* control transfer */
|
|
{ "boot", chirp_emul_boot },
|
|
{ "enter", chirp_emul_enter },
|
|
{ "exit", chirp_emul_exit },
|
|
{ "chain", chirp_emul_chain },
|
|
|
|
/* user interface */
|
|
{ "interpret", chirp_emul_interpret },
|
|
{ "set_callback", chirp_emul_set_callback },
|
|
{ "set_symbol_lookup", chirp_emul_set_symbol_lookup },
|
|
|
|
/* time */
|
|
{ "milliseconds", chirp_emul_milliseconds },
|
|
|
|
{ 0, /* sentinal */ },
|
|
};
|
|
|
|
|
|
/* main handlers */
|
|
|
|
/* Any starting address greater than this is assumed to be an Chirp
|
|
rather than VEA */
|
|
|
|
#ifndef CHIRP_START_ADDRESS
|
|
#define CHIRP_START_ADDRESS 0x80000000
|
|
#endif
|
|
#ifndef CHIRP_LOAD_BASE
|
|
#define CHIRP_LOAD_BASE -1
|
|
#endif
|
|
|
|
|
|
typedef struct _chirp_note_desc {
|
|
signed32 real_mode;
|
|
signed32 real_base;
|
|
signed32 real_size;
|
|
signed32 virt_base;
|
|
signed32 virt_size;
|
|
signed32 load_base;
|
|
} chirp_note_desc;
|
|
|
|
typedef enum {
|
|
note_missing,
|
|
note_found,
|
|
note_correct,
|
|
} note_found_status;
|
|
typedef struct _chirp_note {
|
|
chirp_note_desc desc;
|
|
note_found_status found;
|
|
} chirp_note;
|
|
|
|
typedef struct _chirp_note_head {
|
|
unsigned32 namesz;
|
|
unsigned32 descsz;
|
|
unsigned32 type;
|
|
} chirp_note_head;
|
|
|
|
static void
|
|
map_over_chirp_note(bfd *image,
|
|
asection *sect,
|
|
PTR obj)
|
|
{
|
|
chirp_note *note = (chirp_note*)obj;
|
|
if (strcmp(sect->name, ".note") == 0) {
|
|
chirp_note_head head;
|
|
char name[16];
|
|
/* check the head */
|
|
if (!bfd_get_section_contents(image, sect,
|
|
&head, 0, sizeof(head)))
|
|
return;
|
|
head.namesz = bfd_get_32(image, (void*)&head.namesz);
|
|
head.descsz = bfd_get_32(image, (void*)&head.descsz);
|
|
head.type = bfd_get_32(image, (void*)&head.type);
|
|
if (head.type != 0x1275)
|
|
return;
|
|
/* check the name field */
|
|
if (head.namesz > sizeof(name)) {
|
|
error("chirp: note name too long (%d > %d)\n", (int)head.namesz, sizeof(name));
|
|
}
|
|
if (!bfd_get_section_contents(image, sect,
|
|
name, sizeof(head), head.namesz)) {
|
|
error("chirp: note name unreadable\n");
|
|
}
|
|
if (strcmp(name, "PowerPC") != 0) {
|
|
printf_filtered("chirp: note name (%s) not `PowerPC'\n", name);
|
|
}
|
|
/* check the size */
|
|
if (head.descsz == sizeof(note->desc) - sizeof(signed32)) {
|
|
sim_io_printf_filtered("chirp: note descriptor missing load-base\n");
|
|
}
|
|
else if (head.descsz != sizeof(note->desc)) {
|
|
sim_io_printf_filtered("chirp: note descriptor of wrong size\n");
|
|
note->found = note_found;
|
|
return;
|
|
}
|
|
note->found = note_correct;
|
|
/* get the contents */
|
|
if (!bfd_get_section_contents(image, sect,
|
|
¬e->desc, /* page align start */
|
|
((sizeof(head) + head.namesz) + 3) & ~3,
|
|
head.descsz)) {
|
|
error("chirp: note descriptor unreadable\n");
|
|
}
|
|
note->desc.real_mode = bfd_get_32(image, (void*)¬e->desc.real_mode);
|
|
note->desc.real_base = bfd_get_32(image, (void*)¬e->desc.real_base);
|
|
note->desc.real_size = bfd_get_32(image, (void*)¬e->desc.real_size);
|
|
note->desc.virt_base = bfd_get_32(image, (void*)¬e->desc.virt_base);
|
|
note->desc.virt_size = bfd_get_32(image, (void*)¬e->desc.virt_size);
|
|
if (head.descsz == sizeof(note->desc))
|
|
note->desc.load_base = bfd_get_32(image, (void*)¬e->desc.load_base);
|
|
else
|
|
note->desc.load_base = (signed32)-1;
|
|
}
|
|
}
|
|
|
|
|
|
static os_emul_data *
|
|
emul_chirp_create(device *root,
|
|
bfd *image,
|
|
const char *name)
|
|
{
|
|
os_emul_data *chirp;
|
|
device *node;
|
|
chirp_note note;
|
|
int i;
|
|
|
|
/* Sanity check that this really is the chosen emulation */
|
|
if (name == NULL && image == NULL)
|
|
return NULL;
|
|
if (name != NULL
|
|
&& strcmp(name, "ob") != 0
|
|
&& strcmp(name, "ieee1274") != 0
|
|
&& strcmp(name, "chrp") != 0
|
|
&& strcmp(name, "chirp") != 0
|
|
&& strcmp(name, "openboot") != 0)
|
|
return NULL;
|
|
|
|
/* look for an elf note section, enter its values into the device tree */
|
|
memset(¬e, 0, sizeof(note));
|
|
if (image != NULL)
|
|
bfd_map_over_sections(image, map_over_chirp_note, ¬e);
|
|
if (name == NULL && image != NULL && note.found == note_missing)
|
|
return NULL;
|
|
|
|
/* Assume that it is a chirp emulation */
|
|
|
|
chirp = ZALLOC(os_emul_data);
|
|
chirp->root = root;
|
|
chirp->services = services;
|
|
|
|
/* the root node */
|
|
tree_parse(root, "/name \"gpl,clayton");
|
|
|
|
/* default options */
|
|
emul_add_tree_options(root, image, "chirp", "oea",
|
|
0 /*oea-interrupt-prefix*/);
|
|
|
|
/* hardware */
|
|
emul_add_tree_hardware(root);
|
|
|
|
/* basic information */
|
|
chirp->memory_size
|
|
= tree_find_integer_property(root, "/openprom/options/oea-memory-size");
|
|
chirp->little_endian
|
|
= tree_find_boolean_property(root, "/options/little-endian?");
|
|
chirp->floating_point_available
|
|
= tree_find_boolean_property(root, "/openprom/options/floating-point?");
|
|
chirp->interrupt_prefix =
|
|
tree_find_integer_property(root, "/openprom/options/oea-interrupt-prefix");
|
|
|
|
|
|
/* Perform an interum layout of the openboot firmware in memory */
|
|
|
|
|
|
/* a page for firmware calls */
|
|
chirp->sizeof_code = 4096;
|
|
chirp->code_offset = 0x4000; /* possible space for interrupt table */
|
|
|
|
/* the stack */
|
|
chirp->sizeof_stack = 32 * 1024;
|
|
chirp->stack_offset = chirp->code_offset + chirp->sizeof_code;
|
|
|
|
/* the hash table */
|
|
if (!note.desc.real_mode) {
|
|
chirp->nr_page_table_entry_groups = (chirp->memory_size < 0x800000
|
|
? 1024 /* min allowed */
|
|
: (chirp->memory_size / 4096 / 2));
|
|
chirp->sizeof_htab = chirp->nr_page_table_entry_groups * 64;
|
|
}
|
|
chirp->htab_offset = chirp->stack_offset + chirp->sizeof_stack;
|
|
|
|
/* the actual amount of space needed */
|
|
chirp->real_size = chirp->htab_offset + chirp->sizeof_htab;
|
|
|
|
|
|
/* now go through and see if it fits in what is available */
|
|
|
|
|
|
/* resolve real-mode? */
|
|
if (note.found == note_correct)
|
|
chirp->real_mode = note.desc.real_mode;
|
|
else if (tree_find_property(root, "/options/real-mode?") != NULL)
|
|
chirp->real_mode = tree_find_boolean_property(root, "/options/real-mode?");
|
|
else
|
|
chirp->real_mode = 0;
|
|
if (tree_find_property(root, "/options/real-mode?") != NULL) {
|
|
if (!chirp->real_mode
|
|
!= !tree_find_boolean_property(root, "/options/real-mode?"))
|
|
error("chirp: /options/real-mode? conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/real-mode? %s",
|
|
chirp->real_mode ? "true" : "false");
|
|
|
|
/* resolve real-base */
|
|
if (note.found == note_correct
|
|
&& note.desc.real_base != (signed32)-1)
|
|
chirp->real_base = note.desc.real_base;
|
|
else if (tree_find_property(root, "/options/real-base") != NULL)
|
|
chirp->real_base = tree_find_integer_property(root, "/options/real-base");
|
|
else
|
|
chirp->real_base = chirp->memory_size - chirp->real_size;
|
|
if (tree_find_property(root, "/options/real-base") != NULL) {
|
|
if (chirp->real_base != tree_find_integer_property(root, "/options/real-base"))
|
|
error("chirp: /options/real-base conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/real-base 0x%lx",
|
|
(unsigned long)chirp->real_base);
|
|
|
|
/* resolve real-size */
|
|
if (note.found == note_correct
|
|
&& note.desc.real_size != (signed32)-1
|
|
&& note.desc.real_size != 0
|
|
&& chirp->real_size > note.desc.real_size)
|
|
error("chirp: insufficient physical memory for firmware\n");
|
|
if (tree_find_property(root, "/options/real-size") != NULL) {
|
|
if (chirp->real_size > tree_find_integer_property(root, "/options/real-size"))
|
|
error("chirp: /options/real-size conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/real-size 0x%lx",
|
|
(unsigned long)chirp->real_size);
|
|
|
|
/* resolve virt-base */
|
|
if (chirp->real_mode)
|
|
chirp->virt_base = chirp->real_base;
|
|
else if (note.found == note_correct && note.desc.virt_base != -1)
|
|
chirp->virt_base = note.desc.virt_base;
|
|
else if (tree_find_property(root, "/options/virt-base") != NULL)
|
|
chirp->virt_base = tree_find_integer_property(root, "/options/virt-base");
|
|
else
|
|
chirp->virt_base = CHIRP_START_ADDRESS;
|
|
if (tree_find_property(root, "/options/virt-base") != NULL) {
|
|
unsigned_word virt_base = tree_find_integer_property(root, "/options/virt-base");
|
|
if (virt_base != -1 && chirp->virt_base != virt_base)
|
|
error("chirp: /options/virt-base conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/virt-base 0x%lx",
|
|
chirp->real_mode ? -1 : (unsigned long)chirp->virt_base);
|
|
|
|
/* resolve virt-size */
|
|
chirp->virt_size = chirp->real_size;
|
|
if (note.found == note_correct
|
|
&& note.desc.virt_size != (signed32)-1
|
|
&& note.desc.virt_size != 0
|
|
&& !chirp->real_mode
|
|
&& chirp->virt_size > note.desc.virt_size)
|
|
error("chirp: insufficent virtual memory for firmware\n");
|
|
if (tree_find_property(root, "/options/virt-size") != NULL) {
|
|
if (chirp->virt_size > tree_find_integer_property(root, "/options/virt-size"))
|
|
error("chirp: /options/virt-size conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/virt-size 0x%lx",
|
|
chirp->real_mode ? -1 : (unsigned long)chirp->virt_size);
|
|
|
|
/* resolve load-base */
|
|
if (note.found == note_correct
|
|
&& note.desc.load_base != (signed32)-1)
|
|
chirp->load_base = note.desc.load_base;
|
|
else if (tree_find_property(root, "/options/load-base") != NULL)
|
|
chirp->load_base = tree_find_integer_property(root, "/options/load-base");
|
|
else
|
|
chirp->load_base = CHIRP_LOAD_BASE;
|
|
if (tree_find_property(root, "/options/load-base") != NULL) {
|
|
if (chirp->load_base != tree_find_integer_property(root, "/options/load-base"))
|
|
error("chirp: /options/load-base conflicts with note section\n");
|
|
}
|
|
else
|
|
tree_parse(root, "/options/load-base 0x%lx",
|
|
(unsigned long)chirp->load_base);
|
|
|
|
/* now adjust the preliminary firmware addresses to final values */
|
|
chirp->code_ra = chirp->code_offset + chirp->real_base;
|
|
chirp->stack_ra = chirp->stack_offset + chirp->real_base;
|
|
chirp->htab_ra = chirp->htab_offset + chirp->real_base;
|
|
|
|
/* the virtual addresses. In real mode these are real addresses. */
|
|
|
|
chirp->code_va = chirp->code_offset + chirp->virt_base;
|
|
chirp->stack_va = chirp->stack_offset + chirp->virt_base;
|
|
chirp->htab_va = chirp->htab_offset + chirp->virt_base;
|
|
|
|
chirp->code_client_va = chirp->code_va;
|
|
chirp->code_client_ra = chirp->code_ra;
|
|
|
|
chirp->code_callback_va = chirp->code_client_va + 16;
|
|
chirp->code_callback_ra = chirp->code_client_ra + 16;
|
|
|
|
chirp->code_loop_va = chirp->code_callback_va + 16;
|
|
chirp->code_loop_ra = chirp->code_callback_ra + 16;
|
|
|
|
/* initialization */
|
|
|
|
tree_parse(root, "/openprom/init");
|
|
tree_parse(root, "/openprom/init/register");
|
|
tree_parse(root, "/openprom/init/register/0.pc 0x%lx",
|
|
(unsigned long)bfd_get_start_address(image));
|
|
tree_parse(root, "/openprom/init/register/pc 0x%lx",
|
|
(unsigned long)chirp->code_loop_va);
|
|
tree_parse(root, "/openprom/init/register/msr 0x%x",
|
|
(msr_machine_check_enable
|
|
| (chirp->real_mode
|
|
? 0
|
|
: (msr_instruction_relocate
|
|
| msr_data_relocate))
|
|
| (chirp->little_endian
|
|
? (msr_little_endian_mode
|
|
| msr_interrupt_little_endian_mode)
|
|
: 0)
|
|
| (chirp->floating_point_available
|
|
? msr_floating_point_available
|
|
: 0)
|
|
| (chirp->interrupt_prefix
|
|
? msr_interrupt_prefix
|
|
: 0)
|
|
));
|
|
tree_parse(root, "/openprom/init/register/sdr1 0x%lx",
|
|
(unsigned long)(chirp->htab_ra
|
|
| MASK32(16, 22)
|
|
| ((chirp->sizeof_htab - 1) >> 16)));
|
|
/* make certain that the segment registers map straight through */
|
|
for (i = 0; i < 16; i++) {
|
|
tree_parse(root, "/openprom/init/register/sr%d 0x%lx",
|
|
i, (unsigned long)i);
|
|
}
|
|
|
|
/* establish an initial state for all processors */
|
|
|
|
|
|
/* the client interface address */
|
|
tree_parse(root, "/openprom/init/register/r5 0x%lx",
|
|
(unsigned long)chirp->code_client_va);
|
|
/* a stack */
|
|
tree_parse(root, "/openprom/init/register/sp 0x%lx",
|
|
(unsigned long)(chirp->stack_va + chirp->sizeof_stack - 16));
|
|
/* in chrp mode any arguments end up being concatinated */
|
|
tree_parse(root, "/openprom/init/stack/stack-type chirp");
|
|
|
|
|
|
/* client interface - emul-call followed by return instruction */
|
|
|
|
|
|
node = tree_parse(root, "/openprom/init/data@0x%lx",
|
|
(unsigned long)chirp->code_client_ra);
|
|
tree_parse(node, "./psim,description \"client-interface instruction");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->code_client_ra);
|
|
tree_parse(node, "./data 0x%lx",
|
|
(unsigned long)emul_call_instruction);
|
|
|
|
node = tree_parse(root, "/openprom/init/data@0x%lx",
|
|
(unsigned long)(chirp->code_client_ra + 4));
|
|
tree_parse(node, "./psim,description \"client-interface return instruction");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)(chirp->code_client_ra + 4));
|
|
tree_parse(node, "./data 0x%lx",
|
|
(unsigned long)emul_blr_instruction);
|
|
|
|
|
|
/* return address for client callbacks - an emul-call instruction
|
|
that is again followed by a return instruction */
|
|
|
|
|
|
node = tree_parse(root, "/openprom/init/data@0x%lx",
|
|
(unsigned long)chirp->code_callback_ra);
|
|
tree_parse(node, "./psim,description \"client-callback instruction");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->code_callback_ra);
|
|
tree_parse(node, "./data 0x%lx",
|
|
(unsigned long)emul_call_instruction);
|
|
|
|
node = tree_parse(root, "/openprom/init/data@0x%lx",
|
|
(unsigned long)(chirp->code_callback_ra + 4));
|
|
tree_parse(node, "./psim,description \"client-callback return instruction");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)(chirp->code_callback_ra + 4));
|
|
tree_parse(node, "./data 0x%lx",
|
|
(unsigned long)emul_blr_instruction);
|
|
|
|
/* loop to keep other processors busy */
|
|
|
|
node = tree_parse(root, "/openprom/init/data@0x%lx",
|
|
(unsigned long)chirp->code_loop_ra);
|
|
tree_parse(node, "./psim,description \"processor busy loop");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->code_loop_ra);
|
|
tree_parse(node, "./data 0x%lx",
|
|
(unsigned long)emul_loop_instruction);
|
|
|
|
/* hash table */
|
|
|
|
/* create a hash table */
|
|
|
|
if (!chirp->real_mode) {
|
|
node = tree_parse(root, "/openprom/init/htab@0x%lx",
|
|
(unsigned long)chirp->htab_ra);
|
|
tree_parse(node, "./claim 0");
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->htab_ra);
|
|
tree_parse(node, "./nr-bytes 0x%lx",
|
|
(unsigned long)chirp->sizeof_htab);
|
|
}
|
|
|
|
/* map in the stack */
|
|
|
|
if (!chirp->real_mode) {
|
|
node = tree_parse(root, "/openprom/init/htab/pte@0x%lx",
|
|
(unsigned long)chirp->stack_ra);
|
|
tree_parse(node, "./psim,description \"map in the stack");
|
|
tree_parse(node, "./claim 1");
|
|
tree_parse(node, "./virtual-address 0x%lx",
|
|
(unsigned long)chirp->stack_va);
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->stack_ra);
|
|
tree_parse(node, "./nr-bytes 0x%lx",
|
|
(unsigned long)chirp->sizeof_stack);
|
|
tree_parse(node, "./wimg %d", 0x7);
|
|
tree_parse(node, "./pp %d", 0x2);
|
|
}
|
|
|
|
/* map in the chrp openboot callback code */
|
|
|
|
if (!chirp->real_mode) {
|
|
node = tree_parse(root, "/openprom/init/htab/pte@0x%lx",
|
|
(unsigned long)chirp->code_ra);
|
|
tree_parse(node, "./psim,description \"map in chrp openboot callback code");
|
|
tree_parse(node, "./claim 1");
|
|
tree_parse(node, "./virtual-address 0x%lx",
|
|
(unsigned long)chirp->code_va);
|
|
tree_parse(node, "./real-address 0x%lx",
|
|
(unsigned long)chirp->code_ra);
|
|
tree_parse(node, "./nr-bytes 0x%lx",
|
|
(unsigned long)chirp->sizeof_code);
|
|
tree_parse(node, "./wimg %d", 0x7);
|
|
tree_parse(node, "./pp %d", 0x2);
|
|
}
|
|
|
|
/* map in the program to run */
|
|
|
|
if (chirp->real_mode) {
|
|
node = tree_parse(node, "/openprom/init/load-binary");
|
|
tree_parse(node, "./psim,description \"load the binary");
|
|
tree_parse(node, "./file-name %s", bfd_get_filename(image));
|
|
tree_parse(node, "./claim 1");
|
|
}
|
|
else {
|
|
node = tree_parse(root, "/openprom/init/htab/pte@0x%lx",
|
|
(unsigned long)chirp->load_base);
|
|
tree_parse(node, "./psim,description \"load & map the binary");
|
|
tree_parse(node, "./claim 1");
|
|
tree_parse(node, "./file-name \"%s", bfd_get_filename(image));
|
|
tree_parse(node, "./wimg %d", 0x7);
|
|
tree_parse(node, "./pp %d", 0x2);
|
|
}
|
|
|
|
/* map in the interrupt vectors */
|
|
|
|
if (!chirp->real_mode) {
|
|
node = tree_parse(root, "/openprom/init/htab/pte@0x0");
|
|
tree_parse(node, "./psim,description \"map in interrupt vectors");
|
|
tree_parse(node, "./virtual-address 0x0");
|
|
tree_parse(node, "./real-address 0x0");
|
|
tree_parse(node, "./nr-bytes 0x3000");
|
|
tree_parse(node, "./wimg %d", 0x7);
|
|
tree_parse(node, "./pp %d", 0x2);
|
|
}
|
|
|
|
return chirp;
|
|
}
|
|
|
|
static void
|
|
emul_chirp_init(os_emul_data *emul_data,
|
|
int nr_cpus)
|
|
{
|
|
emul_data->state = serving;
|
|
}
|
|
|
|
static int
|
|
emul_chirp_instruction_call(cpu *processor,
|
|
unsigned_word cia,
|
|
unsigned_word ra,
|
|
os_emul_data *emul_data)
|
|
{
|
|
unsigned_word service_name_addr;
|
|
unsigned_word result;
|
|
char service_buf[32];
|
|
char *service_name;
|
|
chirp_services *service;
|
|
|
|
switch (emul_data->state) {
|
|
|
|
case serving:
|
|
/* we are waiting on an OpenBoot request from the client program
|
|
via the client interface */
|
|
if (cia != emul_data->code_client_va)
|
|
return 0;
|
|
emul_data->return_address = LR;
|
|
emul_data->arguments = cpu_registers(processor)->gpr[3];
|
|
/* try to determine what to do */
|
|
service_name_addr = emul_read_word(cpu_registers(processor)->gpr[3],
|
|
processor, cia);
|
|
service_name = emul_read_string(service_buf, service_name_addr,
|
|
sizeof(service_buf), processor, cia);
|
|
emul_data->n_args = emul_read_word(emul_data->arguments + sizeof(unsigned_cell),
|
|
processor, cia);
|
|
emul_data->n_returns = emul_read_word(emul_data->arguments + 2 * sizeof(unsigned_cell),
|
|
processor, cia);
|
|
/* verify what was passed */
|
|
if (service_name_addr == 0
|
|
|| service_name == NULL) {
|
|
error("OpenFirmware called with invalid (NULL) service name from 0x%lx with args 0x%lx\n",
|
|
(unsigned long)emul_data->return_address,
|
|
(unsigned long)emul_data->arguments);
|
|
}
|
|
if (emul_data->n_args > 6) { /* See iee1275 requirements on nr returns */
|
|
error("OpenFirmware service %s called from 0x%lx with args 0x%lx, too many args (%d)\n",
|
|
(unsigned long)emul_data->return_address,
|
|
(unsigned long)emul_data->arguments,
|
|
emul_data->n_returns);
|
|
}
|
|
if (emul_data->n_returns > 6) {
|
|
error("OpenFirmware service %s called from 0x%lx with args 0x%lx, with too many returns (%d)\n",
|
|
(unsigned long)emul_data->return_address,
|
|
(unsigned long)emul_data->arguments,
|
|
emul_data->n_args);
|
|
}
|
|
/* look it up */
|
|
TRACE(trace_os_emul, ("%s called from 0x%lx with args 0x%lx\n",
|
|
service_name,
|
|
(unsigned long)emul_data->return_address,
|
|
(unsigned long)emul_data->arguments));
|
|
service = services;
|
|
while (service->name != NULL && strcmp(service->name, service_name) != 0)
|
|
service++;
|
|
/* found or not? */
|
|
if (service->name == NULL) {
|
|
error("OpenBoot service `%s' not found\n", service_name);
|
|
TRACE(trace_os_emul, ("%s not found\n", service_name));
|
|
cpu_registers(processor)->gpr[3] = -1;
|
|
}
|
|
else {
|
|
emul_data->service = service;
|
|
/* call upon it */
|
|
result = service->handler(emul_data, processor, cia);
|
|
if (result != 0)
|
|
TRACE(trace_os_emul, ("%s aborted with %ld\n", service_name, (long)result));
|
|
cpu_registers(processor)->gpr[3] = result;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error("emul_chirp_instruction_call() unknown internal state\n");
|
|
result = -1;
|
|
break;
|
|
|
|
}
|
|
|
|
/* return to caller - instruction following this is a function return */
|
|
return 1;
|
|
}
|
|
|
|
const os_emul emul_chirp = {
|
|
"chirp",
|
|
emul_chirp_create,
|
|
emul_chirp_init,
|
|
NULL, /*system_call*/
|
|
emul_chirp_instruction_call,
|
|
0 /*data*/
|
|
};
|
|
|
|
#endif
|