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