1240 lines
31 KiB
C
1240 lines
31 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 _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 "cap.h"
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifndef STATIC_INLINE_EMUL_CHIRP
|
|
#define STATIC_INLINE_EMUL_CHIRP STATIC_INLINE
|
|
#endif
|
|
|
|
|
|
/* 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,
|
|
faulting,
|
|
catching,
|
|
} chirp_emul_state;
|
|
|
|
struct _os_emul_data {
|
|
chirp_emul_state state;
|
|
unsigned_word return_address;
|
|
unsigned_word arguments;
|
|
chirp_services *service;
|
|
unsigned_word serving_instruction_ea;
|
|
unsigned_word catching_instruction_ea;
|
|
cap *phandles;
|
|
device *root;
|
|
chirp_services *services;
|
|
};
|
|
|
|
|
|
/* Read in the argument list and make the most basic check that number
|
|
of argumnets are consistent with what was expected */
|
|
|
|
static int
|
|
chirp_read_args(void *args,
|
|
int sizeof_args,
|
|
int n_args,
|
|
int n_returns,
|
|
os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct base_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
} *base;
|
|
emul_read_buffer(args, data->arguments,
|
|
sizeof_args,
|
|
processor, cia);
|
|
base = (struct base_args*)args;
|
|
if (T2H_4(base->n_args) != n_args || T2H_4(base->n_returns) != n_returns) {
|
|
TRACE(trace_os_emul, ("invalid nr of args - n_args=%ld, n_returns=%ld\n",
|
|
(long)T2H_4(base->n_args),
|
|
(long)T2H_4(base->n_returns)));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* OpenBoot emulation functions */
|
|
|
|
/* client interface */
|
|
|
|
static int
|
|
chirp_emul_test(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct test_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 name; /*string*/
|
|
/*out*/
|
|
unsigned32 missing;
|
|
} args;
|
|
char name[32];
|
|
chirp_services *service = data->services;
|
|
/* read in the arguments */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(name, T2H_4(args.name), sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("test - in - name=`%s'\n", name));
|
|
/* see if we know about the service */
|
|
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));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Device tree */
|
|
|
|
static int
|
|
chirp_emul_peer(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct peer_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 phandle;
|
|
/*out*/
|
|
unsigned32 sibling_phandle;
|
|
} args;
|
|
device *dev;
|
|
device *sibling_dev = NULL;
|
|
/* read in the arguments */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
dev = cap_internal(data->phandles, args.phandle);
|
|
TRACE(trace_os_emul, ("peer - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev))));
|
|
/* find the peer */
|
|
if (dev == NULL && args.phandle != 0)
|
|
return -1;
|
|
if (args.phandle == 0)
|
|
sibling_dev = data->root;
|
|
else
|
|
sibling_dev = device_sibling(dev);
|
|
if (sibling_dev == NULL)
|
|
args.sibling_phandle = 0;
|
|
else
|
|
args.sibling_phandle = cap_external(data->phandles, sibling_dev);
|
|
/* write the arguments back out */
|
|
TRACE(trace_os_emul, ("peer - out - sibling_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.sibling_phandle),
|
|
(unsigned long)sibling_dev,
|
|
(sibling_dev == NULL
|
|
? ""
|
|
: device_name(sibling_dev))));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_child(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct child_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 phandle;
|
|
/*out*/
|
|
unsigned32 child_phandle;
|
|
} args;
|
|
device *dev;
|
|
device *child_dev;
|
|
/* read the arguments in */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
dev = cap_internal(data->phandles, args.phandle);
|
|
TRACE(trace_os_emul, ("child - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev))));
|
|
/* find a child */
|
|
if (dev == (device*)0)
|
|
return -1;
|
|
child_dev = device_child(dev);
|
|
if (child_dev == NULL)
|
|
args.child_phandle = 0;
|
|
else
|
|
args.child_phandle = cap_external(data->phandles, child_dev);
|
|
/* write the result out */
|
|
TRACE(trace_os_emul, ("child - out - child_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.child_phandle),
|
|
(unsigned long)child_dev,
|
|
(child_dev == NULL ? "" : device_name(child_dev))));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_parent(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct parent_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 phandle;
|
|
/*out*/
|
|
unsigned32 parent_phandle;
|
|
} args;
|
|
device *dev;
|
|
device *parent_dev;
|
|
/* read the args in */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
dev = cap_internal(data->phandles, args.phandle);
|
|
TRACE(trace_os_emul, ("parent - in - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev))));
|
|
/* find a parent */
|
|
if (dev == (device*)0)
|
|
return -1;
|
|
parent_dev = device_parent(dev);
|
|
if (parent_dev == NULL)
|
|
args.parent_phandle = 0;
|
|
else
|
|
args.parent_phandle = cap_external(data->phandles, parent_dev);
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("parent - out - parent_phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.parent_phandle),
|
|
(unsigned long)parent_dev,
|
|
(parent_dev == NULL ? "" : device_name(parent_dev))));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_instance_to_package(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: instance-to-package unimplemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_getproplen(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct getproplen_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 phandle;
|
|
unsigned32 name;
|
|
/*out*/
|
|
unsigned32 proplen;
|
|
} args;
|
|
char name[32];
|
|
device *dev;
|
|
const device_property *prop;
|
|
/* read the args in */
|
|
if (chirp_read_args(&args, sizeof(args), 2, 1, data, processor, cia))
|
|
return -1;
|
|
dev = cap_internal(data->phandles, args.phandle);
|
|
/* find our prop and get its length */
|
|
if (dev == (device*)0)
|
|
return -1;
|
|
emul_read_string(name,
|
|
T2H_4(args.name),
|
|
sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("getproplen - in - phandle=0x%lx(0x%lx`%s') name=`%s'\n",
|
|
(unsigned long)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev)),
|
|
name));
|
|
prop = device_find_property(dev, name);
|
|
if (prop == (device_property*)0) {
|
|
args.proplen = -1;
|
|
}
|
|
else {
|
|
args.proplen = H2T_4(prop->sizeof_array);
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("getproplen - out - proplen=%ld\n",
|
|
(unsigned long)T2H_4(args.proplen)));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_getprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct getprop_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 phandle;
|
|
unsigned32 name;
|
|
unsigned32 buf;
|
|
unsigned32 buflen;
|
|
/*out*/
|
|
unsigned32 size;
|
|
} args;
|
|
char name[32];
|
|
device *dev;
|
|
const device_property *prop;
|
|
/* read in the args */
|
|
if (chirp_read_args(&args, sizeof(args), 4, 1, data, processor, cia))
|
|
return -1;
|
|
dev = cap_internal(data->phandles, args.phandle);
|
|
emul_read_string(name,
|
|
T2H_4(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)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev)),
|
|
name,
|
|
(unsigned long)T2H_4(args.buf),
|
|
(unsigned long)T2H_4(args.buflen)));
|
|
/* get the property */
|
|
if (dev == (device*)0)
|
|
return -1;
|
|
prop = device_find_property(dev, name);
|
|
if (prop == (device_property*)0) {
|
|
args.size = -1;
|
|
}
|
|
else {
|
|
int size = T2H_4(args.buflen);
|
|
if (size > prop->sizeof_array)
|
|
size = prop->sizeof_array;
|
|
emul_write_buffer(prop->array, T2H_4(args.buf),
|
|
size,
|
|
processor, cia);
|
|
args.size = H2T_4(size);
|
|
}
|
|
switch (prop->type) {
|
|
case string_property:
|
|
TRACE(trace_os_emul, ("getprop - value=`%s' (string)\n",
|
|
(char*)prop->array));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
/* write back the result */
|
|
TRACE(trace_os_emul, ("getprop - out - size=%ld\n",
|
|
(unsigned long)T2H_4(args.size)));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_nextprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: nextprop not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_setprop(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: setprop not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_canon(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: canon not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_finddevice(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct finddevice_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 device_specifier;
|
|
/*out*/
|
|
unsigned32 phandle;
|
|
} args;
|
|
char device_specifier[1024];
|
|
device *dev;
|
|
/* get the args */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(device_specifier,
|
|
T2H_4(args.device_specifier),
|
|
sizeof(device_specifier),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("finddevice - in - device_specifier=`%s'\n",
|
|
device_specifier));
|
|
/* find the device */
|
|
dev = device_tree_find_device(data->root,
|
|
device_specifier);
|
|
if (dev == (device*)0)
|
|
args.phandle = -1;
|
|
else
|
|
args.phandle = cap_external(data->phandles, dev);
|
|
/* return its phandle */
|
|
TRACE(trace_os_emul, ("finddevice - out - phandle=0x%lx(0x%lx`%s')\n",
|
|
(unsigned long)T2H_4(args.phandle),
|
|
(unsigned long)dev,
|
|
(dev == NULL ? "" : device_name(dev))));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_instance_to_path(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: instance_to_path not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_package_to_path(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: package_to_path not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_call_method(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: call-method implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Device I/O */
|
|
|
|
static int
|
|
chirp_emul_open(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct open_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 device_specifier;
|
|
/*out*/
|
|
unsigned32 ihandle;
|
|
} args;
|
|
char name[1024];
|
|
/* read the args */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
emul_read_string(name,
|
|
T2H_4(args.device_specifier),
|
|
sizeof(name),
|
|
processor, cia);
|
|
TRACE(trace_os_emul, ("open - in - device_specifier=`%s'\n",
|
|
name));
|
|
/* open the device */
|
|
printf_filtered("OpenBoot - open unimplemented for %s\n", name);
|
|
args.ihandle = -1;
|
|
/* return the ihandle result */
|
|
TRACE(trace_os_emul, ("open - out - ihandle=0x%lx\n",
|
|
(unsigned long)T2H_4(args.ihandle)));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_close(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: close not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_read(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct read_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 ihandle;
|
|
unsigned32 addr;
|
|
unsigned32 len;
|
|
/*out*/
|
|
unsigned32 actual;
|
|
} args;
|
|
char buf[1024];
|
|
int actual;
|
|
/* read the args */
|
|
if (chirp_read_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
TRACE(trace_os_emul, ("read - in - ihandle=0x%lx addr=0x%lx len=%ld\n",
|
|
(unsigned long)args.ihandle,
|
|
(unsigned long)T2H_4(args.addr),
|
|
(unsigned long)T2H_4(args.len)));
|
|
/* do the read */
|
|
actual = T2H_4(args.len);
|
|
if (actual >= sizeof(buf))
|
|
actual = sizeof(buf) - 1;
|
|
actual = read(BE2H_4(args.ihandle), buf, actual);
|
|
if (actual >= 0) {
|
|
emul_write_buffer(buf,
|
|
T2H_4(args.addr),
|
|
actual,
|
|
processor, cia);
|
|
args.actual = H2T_4(actual);
|
|
buf[actual] = '\0';
|
|
}
|
|
else {
|
|
args.actual = 0;
|
|
}
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("read - out - actual=%ld `%s'\n",
|
|
(long)T2H_4(args.actual),
|
|
(actual >= 0 ? buf : "")));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_write(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct write_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
unsigned32 ihandle;
|
|
unsigned32 addr;
|
|
unsigned32 len;
|
|
/*out*/
|
|
unsigned32 actual;
|
|
} args;
|
|
char buf[1024];
|
|
int actual;
|
|
/* get the args */
|
|
if (chirp_read_args(&args, sizeof(args), 3, 1, data, processor, cia))
|
|
return -1;
|
|
actual = T2H_4(args.len);
|
|
if (actual >= sizeof(buf))
|
|
actual = sizeof(buf) - 1;
|
|
emul_read_buffer(buf,
|
|
T2H_4(args.addr),
|
|
actual,
|
|
processor, cia);
|
|
buf[actual] = '\0';
|
|
TRACE(trace_os_emul, ("write - in - ihandle=0x%lx `%s' (%ld)\n",
|
|
(unsigned long)args.ihandle, buf, (long)actual));
|
|
/* write it out */
|
|
actual = write(BE2H_4(args.ihandle), buf, actual);
|
|
if (actual < 0)
|
|
args.actual = 0;
|
|
else
|
|
args.actual = H2T_4(actual);
|
|
/* return the result */
|
|
TRACE(trace_os_emul, ("write - out - actual=%ld\n",
|
|
(long)T2H_4(args.actual)));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
processor, cia);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_seek(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: seek not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* memory */
|
|
|
|
static int
|
|
chirp_emul_claim(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: claim not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_release(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: release not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Control transfer */
|
|
|
|
static int
|
|
chirp_emul_boot(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: boot not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_enter(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: enter not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_exit(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
cpu_halt(processor, cia, was_exited, 0); /* always succeeds */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_chain(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: chain not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* user interface */
|
|
|
|
static int
|
|
chirp_emul_interpret(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: interpret not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
chirp_emul_set_callback(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
error("chirp: set_callback 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 not implemented\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Time */
|
|
|
|
static int
|
|
chirp_emul_milliseconds(os_emul_data *data,
|
|
cpu *processor,
|
|
unsigned_word cia)
|
|
{
|
|
struct test_args {
|
|
unsigned32 service;
|
|
unsigned32 n_args;
|
|
unsigned32 n_returns;
|
|
/*in*/
|
|
/*out*/
|
|
unsigned32 ms;
|
|
} args;
|
|
unsigned64 time;
|
|
/* read in the arguments */
|
|
if (chirp_read_args(&args, sizeof(args), 1, 1, data, processor, cia))
|
|
return -1;
|
|
/* make up a number */
|
|
time = event_queue_time(cpu_event_queue(processor)) / 1000000;
|
|
args.ms = H2T_4(time);
|
|
/* write the arguments back out */
|
|
TRACE(trace_os_emul, ("milliseconds - out - ms=%ld\n",
|
|
(unsigned long)T2H_4(args.ms)));
|
|
emul_write_buffer(&args, data->arguments,
|
|
sizeof(args),
|
|
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
|
|
|
|
typedef struct _chirp_note_desc {
|
|
signed32 real_mode;
|
|
signed32 real_base;
|
|
signed32 real_size;
|
|
signed32 virt_base;
|
|
signed32 virt_size;
|
|
} chirp_note_desc;
|
|
|
|
typedef struct _chirp_note {
|
|
chirp_note_desc desc;
|
|
int 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;
|
|
note->found = 1;
|
|
/* check the name field */
|
|
if (head.namesz > sizeof(name)) {
|
|
printf_filtered("open-boot warning: note name too long (%ld)\n",
|
|
(long)head.namesz);
|
|
return;
|
|
}
|
|
if (!bfd_get_section_contents(image, sect,
|
|
name, sizeof(head), head.namesz)) {
|
|
printf_filtered("open-boot warning: note name unreadable\n");
|
|
return;
|
|
}
|
|
if (strcmp(name, "PowerPC") != 0) {
|
|
printf_filtered("open-boot warning: note name (%s) not `PowerPC'\n",
|
|
name);
|
|
return;
|
|
}
|
|
/* get the contents */
|
|
if (head.descsz != sizeof(note->desc)) {
|
|
printf_filtered("open-boot warning: note descriptor of wrong size\n");
|
|
return;
|
|
}
|
|
if (!bfd_get_section_contents(image, sect,
|
|
¬e->desc, /* page align start */
|
|
((sizeof(head) + head.namesz) + 3) & ~3,
|
|
head.descsz)) {
|
|
printf_filtered("open-boot warning: note descriptor unreadable\n");
|
|
return;
|
|
}
|
|
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);
|
|
note->found = 2;
|
|
}
|
|
}
|
|
|
|
|
|
static os_emul_data *
|
|
emul_chirp_create(device *root,
|
|
bfd *image,
|
|
const char *name)
|
|
{
|
|
os_emul_data *data;
|
|
chirp_note note;
|
|
int big_endian;
|
|
|
|
/* 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 */
|
|
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)
|
|
return NULL;
|
|
|
|
/* the root node */
|
|
device_add_string_property(root,
|
|
"name",
|
|
"gpl,clayton");
|
|
|
|
{
|
|
const unsigned_word memory_size = 0x200000;
|
|
|
|
/* the hash table */
|
|
const unsigned nr_page_table_entry_groups = (memory_size < 0x800000
|
|
? 1024 /* min allowed */
|
|
: (memory_size / 4096 / 2));
|
|
const unsigned sizeof_htab = nr_page_table_entry_groups * 64;
|
|
const unsigned_word htab_ra = memory_size - sizeof_htab;
|
|
|
|
/* a page for firmware calls */
|
|
const unsigned_word sizeof_code = 4096;
|
|
const unsigned_word code_ra = htab_ra - sizeof_code;
|
|
|
|
/* the stack */
|
|
const unsigned sizeof_stack = 32 * 1024;
|
|
const unsigned_word stack_ra = code_ra - sizeof_stack;
|
|
|
|
/* the firmware's home */
|
|
const int real_mode = 0;
|
|
/* const unsigned_word real_base = stack_ra; */
|
|
/* const unsigned real_size = memory_size - real_base; */
|
|
const unsigned_word virt_base = CHIRP_START_ADDRESS;
|
|
/* const unsigned virt_size = real_size;*/
|
|
|
|
/* the virtual addresses */
|
|
const unsigned_word stack_va = virt_base;
|
|
const unsigned_word code_va = stack_va + sizeof_stack;
|
|
const unsigned_word code_client_va = code_va;
|
|
const unsigned_word code_callback_va = code_client_va + 16;
|
|
const unsigned_word code_loop_va = code_callback_va + 16;
|
|
const unsigned_word htab_va = code_va + sizeof_code;
|
|
|
|
#ifdef bfd_big_endian /* new bfd */
|
|
big_endian = bfd_big_endian(image);
|
|
#else
|
|
big_endian = image->xvec->byteorder_big_p;
|
|
#endif
|
|
|
|
/* options */
|
|
{
|
|
device *options = device_tree_add_found(root, "/", "options");
|
|
device_add_integer_property(options,
|
|
"smp",
|
|
MAX_NR_PROCESSORS);
|
|
device_add_boolean_property(options,
|
|
"little-endian?",
|
|
!big_endian);
|
|
device_add_string_property(options,
|
|
"env",
|
|
"operating");
|
|
device_add_boolean_property(options,
|
|
"strict-alignment?",
|
|
(WITH_ALIGNMENT == STRICT_ALIGNMENT
|
|
|| !big_endian));
|
|
device_add_boolean_property(options,
|
|
"floating-point?",
|
|
WITH_FLOATING_POINT);
|
|
device_add_string_property(options,
|
|
"os-emul",
|
|
"chirp");
|
|
}
|
|
|
|
/* hardware */
|
|
device_tree_add_found_uw_u_u(root, "/", "memory",
|
|
0, memory_size, access_read_write_exec);
|
|
|
|
/* initialization */
|
|
{
|
|
device *init = device_tree_add_found(root, "/", "init");
|
|
{
|
|
device *init_register = device_tree_add_found(init, "", "register");
|
|
device_add_integer_property(init_register,
|
|
"pc",
|
|
code_loop_va);
|
|
device_add_integer_property(init_register,
|
|
"0.pc",
|
|
bfd_get_start_address(image));
|
|
device_add_integer_property(init_register,
|
|
"sp",
|
|
stack_va + sizeof_stack - 16);
|
|
|
|
/* init the code callback along with a loop for the unused cpu's */
|
|
device_add_integer_property(init_register,
|
|
"r5",
|
|
code_client_va);
|
|
/* client interface */
|
|
device_tree_add_found_uw_u_u(init, "",
|
|
"data",
|
|
code_ra + (code_client_va - code_va),
|
|
4, emul_call_instruction);
|
|
device_tree_add_found_uw_u_u(init, "",
|
|
"data",
|
|
code_ra + (code_client_va - code_va) + 4,
|
|
4, emul_blr_instruction);
|
|
/* callback return address */
|
|
device_tree_add_found_uw_u_u(init, "",
|
|
"data",
|
|
code_ra + (code_callback_va - code_va),
|
|
4, emul_call_instruction);
|
|
/* loop to keep other processors busy */
|
|
device_tree_add_found_uw_u_u(init, "",
|
|
"data",
|
|
code_ra + (code_loop_va - code_va),
|
|
4, emul_loop_instruction);
|
|
device_add_integer_property(init_register,
|
|
"msr",
|
|
(msr_machine_check_enable
|
|
| (real_mode
|
|
? 0
|
|
: (msr_instruction_relocate
|
|
| msr_data_relocate))
|
|
| (big_endian
|
|
? 0
|
|
: (msr_little_endian_mode
|
|
| msr_interrupt_little_endian_mode
|
|
))));
|
|
device_add_integer_property(init_register,
|
|
"sdr1",
|
|
(htab_ra
|
|
| MASK32(16, 22)
|
|
| ((sizeof_htab - 1) >> 16)));
|
|
/* FIXME */
|
|
device_add_integer_property(init_register,
|
|
"sr8",
|
|
0x00fffff8);
|
|
device_add_integer_property(init_register,
|
|
"sr9",
|
|
0x00fffff9);
|
|
|
|
{ /* hash table and vm */
|
|
device *htab_root = device_tree_add_found_uw_u(init, "", "htab",
|
|
htab_ra, sizeof_htab);
|
|
device_tree_add_found_uw_uw_u_u_u(htab_root, "", "pte",
|
|
stack_ra, stack_va, sizeof_stack,
|
|
0x7/*wimg*/, 0x2/*pp*/);
|
|
device_tree_add_found_uw_uw_u_u_u(htab_root, "", "pte",
|
|
code_ra, code_va, sizeof_code,
|
|
0x7/*wimg*/, 0x2/*pp*/);
|
|
device_tree_add_found_uw_uw_u_u_u(htab_root, "", "pte",
|
|
htab_ra, htab_va, sizeof_htab,
|
|
0x7/*wimg*/, 0x2/*pp*/);
|
|
device_tree_add_found_uw_u_u_c(htab_root, "", "pte",
|
|
0x4000, /*magic*/
|
|
0x7/*wimg*/, 0x2/*pp*/,
|
|
bfd_get_filename (image));
|
|
}
|
|
}
|
|
}
|
|
|
|
{ /* chosen options */
|
|
device *chosen = device_tree_add_found(root, "/", "chosen");
|
|
device_add_string_property(chosen,
|
|
"name",
|
|
"chosen");
|
|
device_add_integer_property(chosen,
|
|
"stdin",
|
|
0); /* FIXME: ihandle of stdin */
|
|
device_add_integer_property(chosen,
|
|
"stdout",
|
|
1); /* FIXME: ihandle of stdout */
|
|
device_add_string_property(chosen,
|
|
"bootpath",
|
|
"/disk@0:\\boot");
|
|
device_add_string_property(chosen,
|
|
"bootargs",
|
|
"");
|
|
#if 0
|
|
device_add_integer_property(chosen,
|
|
"memory",
|
|
0); /* FIXME: ihandle of memory */
|
|
device_add_integer_property(chosen,
|
|
"mmu",
|
|
0);
|
|
#endif
|
|
}
|
|
|
|
/* FIXME - should come from the device tree */
|
|
data = ZALLOC(os_emul_data);
|
|
data->serving_instruction_ea = code_client_va;
|
|
data->catching_instruction_ea = code_callback_va;
|
|
data->phandles = cap_create("chirp");
|
|
data->root = root;
|
|
data->services = services;
|
|
return data;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emul_chirp_init(os_emul_data *emul_data,
|
|
int nr_cpus)
|
|
{
|
|
emul_data->state = serving;
|
|
cap_init(emul_data->phandles);
|
|
}
|
|
|
|
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->serving_instruction_ea)
|
|
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);
|
|
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));
|
|
/* look it up */
|
|
service = services;
|
|
while (service->name != NULL && strcmp(service->name, service_name) != 0)
|
|
service++;
|
|
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] = 0;
|
|
cpu_restart(processor, emul_data->return_address);
|
|
}
|
|
emul_data->service = service;
|
|
/* call upon it */
|
|
result = service->handler(emul_data, processor, cia);
|
|
break;
|
|
|
|
case faulting:
|
|
/* We were handling a client request but encountered a page fault
|
|
(or other exception). The fault needs to be passed back to the
|
|
client using the the client suplied call back interface */
|
|
error("emul_chirp_instruction_call() faulting unimplemented\n");
|
|
result = -1;
|
|
break;
|
|
|
|
case catching:
|
|
/* Have called the client (via the callback interface) because
|
|
some fault (hash table miss) occured. The client has finished
|
|
handling this and is now returning */
|
|
error("emul_chirp_instruction_call() catching unimplemented\n");
|
|
result = -1;
|
|
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
|