X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=sim%2Fppc%2Femul_chirp.c;h=f326410b060ddb16922261eaf967402d88504cbd;hb=228c8f4be0c428369ec6b68e25696863d1e62ed7;hp=dc1d8e685dc5db042713c3da720a4418a3d5877d;hpb=93fac32455bb5f7277b85fec5ead13f7abb9fde8;p=deliverable%2Fbinutils-gdb.git diff --git a/sim/ppc/emul_chirp.c b/sim/ppc/emul_chirp.c index dc1d8e685d..f326410b06 100644 --- a/sim/ppc/emul_chirp.c +++ b/sim/ppc/emul_chirp.c @@ -1,21 +1,20 @@ /* This file is part of the program psim. - Copyright (C) 1994-1995, Andrew Cagney + 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 2 of the License, or + 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - + along with this program; if not, see . + */ @@ -28,8 +27,6 @@ #include "emul_generic.h" #include "emul_chirp.h" -#include "cap.h" - #ifdef HAVE_STRING_H #include #else @@ -38,19 +35,44 @@ #endif #endif +#ifdef HAVE_UNISTD_H #include +#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 unsigned_word (chirp_handler) +typedef int (chirp_handler) (os_emul_data *data, cpu *processor, unsigned_word cia); + typedef struct _chirp_services { const char *name; chirp_handler *handler; @@ -61,167 +83,1376 @@ typedef struct _chirp_services { request or waiting on a client callback */ typedef enum { serving, - catching, + 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; - unsigned_word serving_instruction_ea; - unsigned_word catching_instruction_ea; - cap *phandles; 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 */ -static unsigned_word +/* 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 { - unsigned32 service; - unsigned32 n_args; - unsigned32 n_returns; + 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*/ - unsigned32 device_specifier; + unsigned_cell ihandle; + unsigned_cell addr; + unsigned_cell len; /*out*/ - unsigned32 phandle; + unsigned_cell actual; } args; - char device_specifier[1024]; - device *dev; - emul_read_buffer(&args, data->arguments, - sizeof(args), - processor, cia); - if (T2H_4(args.n_args) != 1 || T2H_4(args.n_returns) != 1) + char buf[1024]; + device_instance *ihandle; + /* read the args */ + if (chirp_read_t2h_args(&args, sizeof(args), 3, 1, data, processor, cia)) return -1; - emul_read_string(device_specifier, - T2H_4(args.device_specifier), - sizeof(device_specifier), - processor, cia); - dev = device_tree_find_device(data->root, - device_specifier); - if (dev == (device*)0) - args.phandle = -1; - else - args.phandle = cap_external(data->phandles, dev); - emul_write_buffer(&args, data->arguments, - sizeof(args), - processor, cia); + 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 unsigned_word -chirp_emul_getprop(os_emul_data *data, - cpu *processor, - unsigned_word cia) +static int +chirp_emul_write(os_emul_data *data, + cpu *processor, + unsigned_word cia) { - struct getprop_args { - unsigned32 service; - unsigned32 n_args; - unsigned32 n_returns; + struct write_args { /*in*/ - unsigned32 phandle; - unsigned32 name; - unsigned32 buf; - unsigned32 buflen; + unsigned_cell ihandle; + unsigned_cell addr; + unsigned_cell len; /*out*/ - unsigned32 size; + unsigned_cell actual; } args; - char name[32]; - device *dev; - const device_property *prop; - emul_read_buffer(&args, data->arguments, - sizeof(args), - processor, cia); - if (T2H_4(args.n_args) != 4 || T2H_4(args.n_returns) != 1) - return -1; - /* read in the arguments */ - dev = cap_internal(data->phandles, args.phandle); - if (dev == (device*)0) + 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; - emul_read_string(name, - T2H_4(args.name), - sizeof(name), + actual = args.len; + if (actual >= sizeof(buf)) + actual = sizeof(buf) - 1; + emul_read_buffer(buf, + args.addr, + actual, processor, cia); - prop = device_find_property(dev, name); - if (prop == (device_property*)0) { - args.size = -1; + 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 { - 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); + /* write it out */ + actual = device_instance_write(ihandle, buf, actual); + if (actual < 0) + args.actual = 0; + else + args.actual = actual; } - emul_write_buffer(&args, data->arguments, - sizeof(args), - processor, cia); + /* 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 unsigned_word -chirp_emul_write(os_emul_data *data, +static int +chirp_emul_seek(os_emul_data *data, cpu *processor, unsigned_word cia) { - struct write_args { - unsigned32 service; - unsigned32 n_args; - unsigned32 n_returns; + struct seek_args { /*in*/ - unsigned32 ihandle; - unsigned32 addr; - unsigned32 len; + unsigned_cell ihandle; + unsigned_cell pos_hi; + unsigned_cell pos_lo; /*out*/ - unsigned32 actual; + unsigned_cell status; } args; - char buf[1024]; - int actual; - emul_read_buffer(&args, data->arguments, - sizeof(args), - processor, cia); - if (T2H_4(args.n_args) != 3 || T2H_4(args.n_returns) != 1) + 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 */ - actual = T2H_4(args.len); - if (actual > sizeof(buf)) - actual = sizeof(buf); - emul_read_buffer(buf, - T2H_4(args.addr), - actual, - processor, cia); - /* write it out */ - write(BE2H_4(args.ihandle), buf, actual); - args.actual = H2T_4(actual); - emul_write_buffer(&args, data->arguments, - sizeof(args), - processor, cia); + 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 unsigned_word +static int chirp_emul_exit(os_emul_data *data, - cpu *processor, - unsigned_word cia) + 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_emul_exit not implemnented\n"); + error("chirp: chain method not implemented\n"); return 0; } -chirp_services services[] = { - { "finddevice", chirp_emul_finddevice }, +/* 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 */ }, }; @@ -234,6 +1465,10 @@ chirp_services services[] = { #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; @@ -241,11 +1476,17 @@ typedef struct _chirp_note_desc { 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; - int found; + note_found_status found; } chirp_note; typedef struct _chirp_note_head { @@ -272,26 +1513,43 @@ map_over_chirp_note(bfd *image, 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)) - return; + 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)) - return; - if (strcmp(name, "PowerPC") != 0) + 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, sizeof(head) + head.namesz, - head.descsz)) - return; + ¬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); - note->found = 2; + 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; } } @@ -301,8 +1559,10 @@ emul_chirp_create(device *root, bfd *image, const char *name) { - os_emul_data *data; + 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) @@ -315,149 +1575,360 @@ emul_chirp_create(device *root, && strcmp(name, "openboot") != 0) return NULL; - /* look for an elf note section */ + /* 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) + if (name == NULL && image != NULL && note.found == note_missing) return NULL; - { - 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 htab_va = code_va + sizeof_code; - - /* 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?", - !image->xvec->byteorder_big_p); - device_add_string_property(options, - "env", - "operating"); - device_add_boolean_property(options, - "strict-alignment?", - (WITH_ALIGNMENT == STRICT_ALIGNMENT - || !image->xvec->byteorder_big_p)); - 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", - bfd_get_start_address(image)); - device_add_integer_property(init_register, - "sp", - stack_va + sizeof_stack - 16); - - /* init the code callback */ - device_add_integer_property(init_register, - "r5", - code_va); - device_tree_add_found_uw_u_u(init, "", "data", code_ra, 4, 0x1); - device_tree_add_found_uw_u_u(init, "", "data", code_ra+16, 4, 0x1); - device_add_integer_property(init_register, - "msr", - (msr_machine_check_enable - | (real_mode - ? 0 - : (msr_instruction_relocate - | msr_data_relocate)) - | (image->xvec->byteorder_big_p - ? 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_integer_property(chosen, - "stdout", - 1); - } - - /* FIXME - should come from the device tree */ - data = ZALLOC(os_emul_data); - data->serving_instruction_ea = CHIRP_START_ADDRESS + sizeof_stack;; - data->catching_instruction_ea = CHIRP_START_ADDRESS + sizeof_stack + 16; - data->phandles = cap_create("chirp"); - data->root = root; - return data; + /* 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 @@ -465,14 +1936,13 @@ 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 cia, + unsigned_word ra, + os_emul_data *emul_data) { unsigned_word service_name_addr; unsigned_word result; @@ -481,9 +1951,11 @@ emul_chirp_instruction_call(cpu *processor, chirp_services *service; switch (emul_data->state) { + case serving: - /* verify then capture the current request */ - if (cia != emul_data->serving_instruction_ea) + /* 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]; @@ -492,29 +1964,61 @@ emul_chirp_instruction_call(cpu *processor, 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) { - cpu_registers(processor)->gpr[3] = 0; - cpu_restart(processor, emul_data->return_address); + 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; } - emul_data->service = service; - TRACE(trace_os_emul, ("%s called from 0x%lx\n", - service->name, emul_data->return_address)); - /* call upon it */ - result = service->handler(emul_data, processor, cia); break; + default: error("emul_chirp_instruction_call() unknown internal state\n"); result = -1; break; + } - /* return to caller */ - cpu_registers(processor)->gpr[3] = result; - cpu_restart(processor, emul_data->return_address); + /* return to caller - instruction following this is a function return */ return 1; }