diff options
author | Michael Meissner <gnu@the-meissners.org> | 1995-12-15 20:20:13 +0000 |
---|---|---|
committer | Michael Meissner <gnu@the-meissners.org> | 1995-12-15 20:20:13 +0000 |
commit | 93fac32455bb5f7277b85fec5ead13f7abb9fde8 (patch) | |
tree | ed4ba905547982e681b142bc025ee5b78fe8af7f /sim/ppc/emul_chirp.c | |
parent | ee68a042d20897e3164f96954612ccba80d70426 (diff) | |
download | fsf-binutils-gdb-93fac32455bb5f7277b85fec5ead13f7abb9fde8.zip fsf-binutils-gdb-93fac32455bb5f7277b85fec5ead13f7abb9fde8.tar.gz fsf-binutils-gdb-93fac32455bb5f7277b85fec5ead13f7abb9fde8.tar.bz2 |
Changes from Andrew
Diffstat (limited to 'sim/ppc/emul_chirp.c')
-rw-r--r-- | sim/ppc/emul_chirp.c | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/sim/ppc/emul_chirp.c b/sim/ppc/emul_chirp.c new file mode 100644 index 0000000..dc1d8e6 --- /dev/null +++ b/sim/ppc/emul_chirp.c @@ -0,0 +1,530 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _EMUL_CHIRP_C_ +#define _EMUL_CHIRP_C_ + +/* Note: this module is called via a table. There is no benefit in + making it inline */ + +#include "emul_generic.h" +#include "emul_chirp.h" + +#include "cap.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#include <unistd.h> + +#ifndef STATIC_INLINE_EMUL_CHIRP +#define STATIC_INLINE_EMUL_CHIRP STATIC_INLINE +#endif + + +/* Descriptor of the open boot services being emulated */ + +typedef unsigned_word (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, + catching, +} chirp_emul_state; + +struct _os_emul_data { + chirp_emul_state state; + unsigned_word return_address; + unsigned_word arguments; + chirp_services *service; + unsigned_word serving_instruction_ea; + unsigned_word catching_instruction_ea; + cap *phandles; + device *root; +}; + + +/* OpenBoot emulation functions */ + +static unsigned_word +chirp_emul_finddevice(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct finddevice_args { + unsigned32 service; + unsigned32 n_args; + unsigned32 n_returns; + /*in*/ + unsigned32 device_specifier; + /*out*/ + unsigned32 phandle; + } args; + char device_specifier[1024]; + device *dev; + emul_read_buffer(&args, data->arguments, + sizeof(args), + processor, cia); + if (T2H_4(args.n_args) != 1 || T2H_4(args.n_returns) != 1) + 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); + return 0; +} + +static unsigned_word +chirp_emul_getprop(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct getprop_args { + unsigned32 service; + unsigned32 n_args; + unsigned32 n_returns; + /*in*/ + unsigned32 phandle; + unsigned32 name; + unsigned32 buf; + unsigned32 buflen; + /*out*/ + unsigned32 size; + } args; + char name[32]; + device *dev; + const device_property *prop; + 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) + return -1; + emul_read_string(name, + T2H_4(args.name), + sizeof(name), + processor, cia); + prop = device_find_property(dev, name); + if (prop == (device_property*)0) { + args.size = -1; + } + else { + int size = T2H_4(args.buflen); + if (size > prop->sizeof_array) + size = prop->sizeof_array; + emul_write_buffer(prop->array, T2H_4(args.buf), + size, + processor, cia); + args.size = H2T_4(size); + } + emul_write_buffer(&args, data->arguments, + sizeof(args), + processor, cia); + return 0; +} + +static unsigned_word +chirp_emul_write(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + struct write_args { + unsigned32 service; + unsigned32 n_args; + unsigned32 n_returns; + /*in*/ + unsigned32 ihandle; + unsigned32 addr; + unsigned32 len; + /*out*/ + unsigned32 actual; + } args; + char buf[1024]; + int actual; + emul_read_buffer(&args, data->arguments, + sizeof(args), + processor, cia); + if (T2H_4(args.n_args) != 3 || T2H_4(args.n_returns) != 1) + return -1; + /* 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); + return 0; +} + +static unsigned_word +chirp_emul_exit(os_emul_data *data, + cpu *processor, + unsigned_word cia) +{ + error("chirp_emul_exit not implemnented\n"); + return 0; +} + + +chirp_services services[] = { + { "finddevice", chirp_emul_finddevice }, + { "getprop", chirp_emul_getprop }, + { "write", chirp_emul_write }, + { "exit", chirp_emul_exit }, + { 0, /* sentinal */ }, +}; + + +/* main handlers */ + +/* Any starting address greater than this is assumed to be an Chirp + rather than VEA */ + +#ifndef CHIRP_START_ADDRESS +#define CHIRP_START_ADDRESS 0x80000000 +#endif + +typedef struct _chirp_note_desc { + signed32 real_mode; + signed32 real_base; + signed32 real_size; + signed32 virt_base; + signed32 virt_size; +} chirp_note_desc; + +typedef struct _chirp_note { + chirp_note_desc desc; + int found; +} chirp_note; + +typedef struct _chirp_note_head { + unsigned32 namesz; + unsigned32 descsz; + unsigned32 type; +} chirp_note_head; + +static void +map_over_chirp_note(bfd *image, + asection *sect, + PTR obj) +{ + chirp_note *note = (chirp_note*)obj; + if (strcmp(sect->name, ".note") == 0) { + chirp_note_head head; + char name[16]; + /* check the head */ + if (!bfd_get_section_contents(image, sect, + &head, 0, sizeof(head))) + return; + head.namesz = bfd_get_32(image, (void*)&head.namesz); + head.descsz = bfd_get_32(image, (void*)&head.descsz); + head.type = bfd_get_32(image, (void*)&head.type); + if (head.type != 0x1275) + return; + note->found = 1; + /* check the name field */ + if (head.namesz > sizeof(name)) + return; + if (!bfd_get_section_contents(image, sect, + name, sizeof(head), head.namesz)) + return; + if (strcmp(name, "PowerPC") != 0) + return; + /* get the contents */ + if (!bfd_get_section_contents(image, sect, + ¬e->desc, sizeof(head) + head.namesz, + head.descsz)) + return; + note->desc.real_mode = bfd_get_32(image, (void*)¬e->desc.real_mode); + note->desc.real_base = bfd_get_32(image, (void*)¬e->desc.real_base); + note->desc.real_size = bfd_get_32(image, (void*)¬e->desc.real_size); + note->desc.virt_base = bfd_get_32(image, (void*)¬e->desc.virt_base); + note->desc.virt_size = bfd_get_32(image, (void*)¬e->desc.virt_size); + note->found = 2; + } +} + + +static os_emul_data * +emul_chirp_create(device *root, + bfd *image, + const char *name) +{ + os_emul_data *data; + chirp_note note; + + /* Sanity check that this really is the chosen emulation */ + if (name == NULL && image == NULL) + return NULL; + if (name != NULL + && strcmp(name, "ob") != 0 + && strcmp(name, "ieee1274") != 0 + && strcmp(name, "chrp") != 0 + && strcmp(name, "chirp") != 0 + && strcmp(name, "openboot") != 0) + return NULL; + + /* look for an elf note section */ + memset(¬e, 0, sizeof(note)); + if (image != NULL) + bfd_map_over_sections(image, map_over_chirp_note, ¬e); + if (name == NULL && image != NULL && !note.found) + return NULL; + + { + 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; + } +} + +static void +emul_chirp_init(os_emul_data *emul_data, + int nr_cpus) +{ + emul_data->state = serving; + cap_init(emul_data->phandles); +} + +static int +emul_chirp_instruction_call(cpu *processor, + unsigned_word cia, + unsigned_word ra, + os_emul_data *emul_data) +{ + unsigned_word service_name_addr; + unsigned_word result; + char service_buf[32]; + char *service_name; + chirp_services *service; + + switch (emul_data->state) { + case serving: + /* verify then capture the current request */ + if (cia != emul_data->serving_instruction_ea) + return 0; + emul_data->return_address = LR; + emul_data->arguments = cpu_registers(processor)->gpr[3]; + /* try to determine what to do */ + service_name_addr = emul_read_word(cpu_registers(processor)->gpr[3], + processor, cia); + service_name = emul_read_string(service_buf, service_name_addr, + sizeof(service_buf), processor, cia); + /* look it up */ + service = services; + while (service->name != NULL && strcmp(service->name, service_name) != 0) + service++; + if (service->name == NULL) { + cpu_registers(processor)->gpr[3] = 0; + cpu_restart(processor, emul_data->return_address); + } + 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 1; +} + +const os_emul emul_chirp = { + "chirp", + emul_chirp_create, + emul_chirp_init, + NULL, /*system_call*/ + emul_chirp_instruction_call, + 0 /*data*/ +}; + +#endif |