diff options
Diffstat (limited to 'sim/ppc/emul_chirp.c')
-rw-r--r-- | sim/ppc/emul_chirp.c | 2010 |
1 files changed, 2010 insertions, 0 deletions
diff --git a/sim/ppc/emul_chirp.c b/sim/ppc/emul_chirp.c new file mode 100644 index 0000000..c9bb89c --- /dev/null +++ b/sim/ppc/emul_chirp.c @@ -0,0 +1,2010 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1997, 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" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#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); + emul_read_string(previous, + args.previous, + sizeof(previous), + processor, cia); + 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) { + args.flag = -1; /* name invalid */ + } + else { + const device_property *next_prop; + 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 = CHIRP_LOAD_BASE; + } +} + + +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); + } + + 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 |