diff options
Diffstat (limited to 'sim/ppc/devices.c')
-rw-r--r-- | sim/ppc/devices.c | 1597 |
1 files changed, 1389 insertions, 208 deletions
diff --git a/sim/ppc/devices.c b/sim/ppc/devices.c index 96b8109..983b693 100644 --- a/sim/ppc/devices.c +++ b/sim/ppc/devices.c @@ -31,51 +31,291 @@ #include <string.h> #include <stdlib.h> #include <fcntl.h> +#include <signal.h> +#include <stdarg.h> #include "basics.h" -#include "device_tree.h" #include "devices.h" #include "events.h" -#include "cpu.h" /* drats */ +#include "cpu.h" + +#include "bfd.h" /* Helper functions */ +/* Generic device init: Attaches the device of size <nr_bytes> (taken + from <name>@<int>,<nr_bytes>) to its parent at address zero and + with read/write access. */ + STATIC_INLINE_DEVICES void -parse_device_address(char *name, - unsigned *base, - unsigned *flags) +generic_init_callback(const device *me, + psim *system) { - /* extract the two arguments */ - name = strchr(name, '@'); - if (name == NULL) - error("missing address for device %s\n", name); - name++; - *base = strtol(name, &name, 0); - *flags = (*name == ',' - ? strtol(name+1, &name, 0) - : 0); + unsigned_word addr; + unsigned nr_bytes; + if (scand_uw_u(me->name, &addr, &nr_bytes) != 2) + error("generic_init_callback() invalid nr_bytes in %s\n", me->name); + me->parent->callback->attach_address(me->parent, + me->name, + attach_callback, + 0 /*address_space*/, + addr, + nr_bytes, + access_read_write, + me); } -/* Simple console device: + +/* inimplemented versions of each function */ - Implements a simple text output device that is attached to stdout - of the process running the simulation. The devices has four - word registers: +INLINE_DEVICES void +unimp_device_init(const device *me, + psim *system) +{ + error("device_init_callback for %s not implemented\n", me->name); +} - 0: read - 4: read-status - 8: write - c: write-status +INLINE_DEVICES void +unimp_device_attach_address(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + error("device_attach_address_callback for %s not implemented\n", me->name); +} - Where a nonzero status register indicates that the device is ready - (input fifo contains a character or output fifo has space). +INLINE_DEVICES void +unimp_device_detach_address(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + error("device_detach_address_callback for %s not implemented\n", me->name); +} - Illustrates: Mapping read/write to device operations onto actual - registers. +INLINE_DEVICES unsigned +unimp_device_io_read_buffer(const device *me, + void *dest, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + error("device_io_read_buffer_callback for %s not implemented\n", me->name); + return 0; +} - */ +INLINE_DEVICES unsigned +unimp_device_io_write_buffer(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + error("device_io_write_buffer_callback for %s not implemented\n", me->name); + return 0; +} + +INLINE_DEVICES unsigned +unimp_device_dma_read_buffer(const device *me, + void *target, + int address_space, + unsigned_word addr, + unsigned nr_bytes) +{ + error("device_dma_read_buffer_callback for %s not implemented\n", me->name); + return 0; +} + +INLINE_DEVICES unsigned +unimp_device_dma_write_buffer(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + error("device_dma_write_buffer_callback for %s not implemented\n", me->name); + return 0; +} + +INLINE_DEVICES void +unimp_device_attach_interrupt(const device *me, + const device *who, + int interrupt_line, + const char *name) +{ + error("device_attach_interrupt_callback for %s not implemented\n", me->name); +} + +INLINE_DEVICES void +unimp_device_detach_interrupt(const device *me, + const device *who, + int interrupt_line, + const char *name) +{ + error("device_detach_interrupt_callback for %s not implemented\n", me->name); +} + +INLINE_DEVICES void +unimp_device_interrupt(const device *me, + const device *who, + int interrupt_line, + int interrupt_status, + cpu *processor, + unsigned_word cia) +{ + error("device_interrupt_callback for %s not implemented\n", me->name); +} + +INLINE_DEVICES void +unimp_device_interrupt_ack(const device *me, + int interrupt_line, + int interrupt_status) +{ + error("device_interrupt_ack_callback for %s not implemented\n", me->name); +} + +INLINE_DEVICES void +unimp_device_ioctl(const device *me, + psim *system, + cpu *processor, + unsigned_word cia, + ...) +{ + error("device_ioctl_callback for %s not implemented\n", me->name); +} + + + +/* ignore/passthrough versions of each function */ + +INLINE_DEVICES void +ignore_device_init(const device *me, + psim *system) +{ + /*null*/ +} + +INLINE_DEVICES void +pass_device_attach_address(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + me->parent->callback->attach_address(me->parent, name, type, + address_space, addr, nr_bytes, + access, + who); +} + +INLINE_DEVICES void +pass_device_detach_address(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + me->parent->callback->detach_address(me->parent, name, type, + address_space, addr, nr_bytes, access, + who); +} + +INLINE_DEVICES unsigned +pass_device_dma_read_buffer(const device *me, + void *target, + int address_space, + unsigned_word addr, + unsigned nr_bytes) +{ + return me->parent->callback->dma_read_buffer(me->parent, target, + address_space, addr, nr_bytes); +} + +INLINE_DEVICES unsigned +pass_device_dma_write_buffer(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + int violate_read_only_section) +{ + return me->parent->callback->dma_write_buffer(me->parent, source, + address_space, addr, + nr_bytes, + violate_read_only_section); +} + +INLINE_DEVICES void +pass_device_attach_interrupt(const device *me, + const device *who, + int interrupt_line, + const char *name) +{ + me->parent->callback->attach_interrupt(me->parent, who, + interrupt_line, name); +} + +INLINE_DEVICES void +pass_device_detach_interrupt(const device *me, + const device *who, + int interrupt_line, + const char *name) +{ + me->parent->callback->detach_interrupt(me->parent, who, + interrupt_line, name); +} + + +INLINE_DEVICES void +pass_device_interrupt(const device *me, + const device *who, + int interrupt_line, + int interrupt_status, + cpu *processor, + unsigned_word cia) +{ + me->parent->callback->interrupt(me->parent, who, + interrupt_line, interrupt_status, + processor, cia); +} + + + +/* Simple console device: console@)x<address>,16 + + Input characters are taken from the keyboard, output characters + sent to the terminal. Echoing of characters is not disabled. + + The device has four registers: + + 0x0: read + 0x4: read-status + 0x8: write + 0xC: write-status + + Where a nonzero status register indicates that the device is ready + (input fifo contains a character or output fifo has space). */ typedef struct _console_buffer { char buffer; @@ -84,8 +324,6 @@ typedef struct _console_buffer { } console_buffer; typedef struct _console_device { - unsigned_word my_base_address; - int interrupt_delay; console_buffer input; console_buffer output; } console_device; @@ -100,24 +338,28 @@ typedef enum { } console_offsets; -STATIC_INLINE_DEVICES unsigned64 -console_read_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - cpu *processor, - unsigned_word cia) +STATIC_INLINE_DEVICES unsigned +console_io_read_buffer_callback(const device *me, + void *dest, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) { - console_device *con = (console_device*)device->data; + console_device *console = (console_device*)me->data; + unsigned_1 val; TRACE(trace_console_device, - ("device=0x%x, base=0x%x, nr_bytes=%d\n", - device, base, nr_bytes)); + ("device=0x%x, addr=0x%x, nr_bytes=%d\n", + me, addr, nr_bytes)); - /* handle the request */ + /* determine what was read */ - switch (base & console_offset_mask) { + switch (addr) { case console_read_buffer: - return con->input.buffer; + val = console->input.buffer; + break; case console_read_status: { /* check for input */ @@ -127,124 +369,130 @@ console_read_callback(device_node *device, flags = fcntl(0, F_GETFL, 0); if (flags == -1) { perror("console"); - return 0; + val = 0; + break; } /* temp, disable blocking IO */ status = fcntl(0, F_SETFL, flags | O_NDELAY); if (status == -1) { perror("console"); - return 0; + val = 0; + break; } /* try for input */ - status = read(0, &con->input.buffer, 1); + status = read(0, &console->input.buffer, 1); if (status == 1) { - con->input.status = 1; + console->input.status = 1; } else { - con->input.status = 0; + console->input.status = 0; } /* return to regular vewing */ fcntl(0, F_SETFL, flags); } - return con->input.status; + val = console->input.status; + break; case console_write_buffer: - return con->output.buffer; + val = console->output.buffer; + break; case console_write_status: - return con->output.status; + val = console->output.status; + break; default: error("console_read_callback() internal error\n"); - return 0; + val = 0; + break; } + bzero(dest, nr_bytes); + *(unsigned_1*)dest = val; + return nr_bytes; } -STATIC_INLINE_DEVICES void -console_write_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - unsigned64 val, - cpu *processor, - unsigned_word cia) +STATIC_INLINE_DEVICES unsigned +console_io_write_buffer_callback(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) { - console_device *con = (console_device*)device->data; + console_device *console = (console_device*)me->data; + unsigned_1 val = *(unsigned8*)source; TRACE(trace_console_device, - ("device=0x%x, base=0x%x, nr_bytes=%d, val=0x%x\n", - device, base, nr_bytes, val)); + ("device=0x%x, addr=0x%x, nr_bytes=%d, val=%d\n", + me, addr, nr_bytes, val)); - /* check for bus error */ - if (base & 0x3) { - error("%s - misaligned base address, base=0x%x, nr_bytes=%d\n", - "console_write_callback", base, nr_bytes); - } - - switch (base & console_offset_mask) { - case console_read_buffer: con->input.buffer = val; break; - case console_read_status: con->input.status = val; break; + switch (addr) { + case console_read_buffer: + console->input.buffer = val; + break; + case console_read_status: + console->input.status = val; + break; case console_write_buffer: TRACE(trace_console_device, ("<%c:%d>", val, val)); - printf_filtered("%c", val); - con->output.buffer = val; - con->output.status = 1; + printf_filtered("%c",val) ; + console->output.buffer = val; + console->output.status = 1; break; case console_write_status: - con->output.status = val; + console->output.status = val; break; + default: + error("console_write_callback() internal error\n"); } - + + return nr_bytes; } -static device_callbacks console_callbacks = { - console_read_callback, - console_write_callback, + +static device_callbacks const console_callbacks = { + generic_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + console_io_read_buffer_callback, + console_io_write_buffer_callback, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, }; -STATIC_INLINE_DEVICES device_node * -console_create(device_node *parent, - char *name) -{ - device_node *device; - unsigned address_base; - unsigned address_flags; +STATIC_INLINE_DEVICES const device * +console_create(const char *name, + const device *parent) +{ /* create the descriptor */ console_device *console = ZALLOC(console_device); - /* extract the two arguments */ - parse_device_address(name, &address_base, &address_flags); - /* fill in the details */ - console->my_base_address = address_base; - console->interrupt_delay = address_flags; console->output.status = 1; console->output.buffer = '\0'; console->input.status = 0; console->input.buffer = '\0'; /* insert into the device tree along with its address info */ - device = device_node_create(parent, name, sequential_device, - &console_callbacks, console); - device_node_add_address(device, - address_base, - console_size, - device_is_read_write_exec, - NULL); - - return device; + return device_create_from(name, + console, /* data */ + &console_callbacks, + parent); } -static device_descriptor console_descriptor = { - "console", - console_create, -}; - -/* ICU device: + +/* ICU device: icu@0x<address>,4 Single 4 byte register. Read returns processor number. Write interrupts specified processor. @@ -256,55 +504,513 @@ static device_descriptor console_descriptor = { device doesn't pass interrupt events to its parent. Instead it passes them back to its self. */ -STATIC_INLINE_DEVICES unsigned64 -icu_read_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - cpu *processor, - unsigned_word cia) +STATIC_INLINE_DEVICES unsigned +icu_io_read_buffer_callback(const device *me, + void *dest, + int address_space, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) { + unsigned_1 val; TRACE(trace_icu_device, ("device=0x%x, base=0x%x, nr_bytes=%d\n", - device, base, nr_bytes)); - return cpu_nr(processor); + me, base, nr_bytes)); + val = cpu_nr(processor); + bzero(dest, nr_bytes); + *(unsigned_1*)dest = val; + return nr_bytes; } -STATIC_INLINE_DEVICES void -icu_write_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - unsigned64 val, - cpu *processor, - unsigned_word cia) + +STATIC_INLINE_DEVICES unsigned +icu_io_write_buffer_callback(const device *me, + const void *source, + int address_space, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) { psim *system = cpu_system(processor); - device_node *parent = device; /* NB: normally would be device->parent */ + unsigned_1 val = H2T_1(*(unsigned_1*)source); TRACE(trace_icu_device, ("device=0x%x, base=0x%x, nr_bytes=%d, val=0x%x\n", - device, base, nr_bytes, val)); + me, base, nr_bytes, val)); /* tell the parent device that the interrupt lines have changed. For this fake ICU. The interrupt lines just indicate the cpu to interrupt next */ - parent->callbacks->interrupt_callback(parent, val, device, processor, cia); + me->parent->callback->interrupt(me->parent, me, + val, val, + processor, cia); + return nr_bytes; } + +static device_callbacks const icu_callbacks = { + generic_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + icu_io_read_buffer_callback, + icu_io_write_buffer_callback, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + + + +/* HALT device: halt@0x<address>,4 + + With real hardware, the processor operation is normally terminated + through a reset. This device illustrates how a reset device could + be attached to an address */ + + +STATIC_INLINE_DEVICES unsigned +halt_io_read_buffer_callback(const device *me, + void *dest, + int address_space, + unsigned_word base, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + cpu_halt(processor, cia, was_exited, 0); + return 0; +} + + +STATIC_INLINE_DEVICES unsigned +halt_io_write_buffer_callback(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + cpu_halt(processor, cia, was_exited, 0); + return 0; +} + + +static device_callbacks const halt_callbacks = { + generic_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + halt_io_read_buffer_callback, + halt_io_write_buffer_callback, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + + + +/* Register init device: register@<name>,0x<value>[,<processor>] + + This strange device is used to initialize the processors registers + as part of the initialization. */ + +STATIC_INLINE_DEVICES void +register_init_callback(const device *me, + psim *system) +{ + char name[100]; + unsigned_word value; + unsigned which_cpu; + int status; + status = scand_c_uw_u(me->name, name, &value, &which_cpu); + switch (status) { + case 2: /* register@<name>,<value> */ + psim_write_register(system, -1, &value, name, cooked_transfer); + break; + case 3: /* register@<name>,<value>,<processor> */ + psim_write_register(system, which_cpu, &value, name, cooked_transfer); + break; + default: + error("register_init_callback() invalid register init %s\n", me->name); + break; + } +} + + +static device_callbacks const register_callbacks = { + register_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + + + +/* VEA VM device: vm@0x<stack-base>,<nr_bytes> + + A VEA mode device. This sets its self up as the default memory + device capturing all accesses (reads/writes) to currently unmapped + addresses. If the unmaped access falls within unallocated stack or + heap address ranges then memory is allocated and the access is + allowed to continue. + + During init phase, this device expects to receive `attach' requests + from its children for the text/data/bss memory areas. Typically, + this would be done by the binary device. + + STACK: The location of the stack in memory is specified as part of + the devices name. Unmaped accesses that fall within the stack + space result in the allocated stack being grown downwards so that + it includes the page of the culprit access. + + HEAP: During initialization, the vm device monitors all `attach' + operations from its children using this to determine the initial + location of the heap. The heap is then extended by system calls + that frob the heap upper bound variable (see system.c). */ + + +typedef struct _vm_device { + /* area of memory valid for stack addresses */ + unsigned_word stack_base; /* min possible stack value */ + unsigned_word stack_bound; + unsigned_word stack_lower_limit; + /* area of memory valid for heap addresses */ + unsigned_word heap_base; + unsigned_word heap_bound; + unsigned_word heap_upper_limit; +} vm_device; + + STATIC_INLINE_DEVICES void -icu_do_interrupt(event_queue *queue, - void *data) +vm_init_callback(const device *me, + psim *system) +{ + vm_device *vm = (vm_device*)me->data; + + /* revert the stack/heap variables to their defaults */ + vm->stack_lower_limit = vm->stack_bound; + vm->heap_base = 0; + vm->heap_bound = 0; + vm->heap_upper_limit = 0; + + /* establish this device as the default memory handler */ + me->parent->callback->attach_address(me->parent, + me->name, + attach_default, + 0 /*address space - ignore*/, + 0 /*addr - ignore*/, + 0 /*nr_bytes - ignore*/, + access_read_write /*access*/, + me); +} + + +STATIC_INLINE_DEVICES void +vm_attach_address(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + vm_device *vm = (vm_device*)me->data; + /* update end of bss if necessary */ + if (vm->heap_base < addr + nr_bytes) { + vm->heap_base = addr + nr_bytes; + vm->heap_bound = addr + nr_bytes; + vm->heap_upper_limit = addr + nr_bytes; + } + me->parent->callback->attach_address(me->parent, + "vm@0x0,0", /* stop remap */ + attach_raw_memory, + 0 /*address space*/, + addr, + nr_bytes, + access, + me); +} + + +STATIC_INLINE_DEVICES unsigned +add_vm_space(const device *me, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + vm_device *vm = (vm_device*)me->data; + unsigned_word block_addr; + unsigned block_nr_bytes; + + /* an address in the stack area, allocate just down to the addressed + page */ + if (addr >= vm->stack_base && addr < vm->stack_lower_limit) { + block_addr = FLOOR_PAGE(addr); + block_nr_bytes = vm->stack_lower_limit - block_addr; + vm->stack_lower_limit = block_addr; + } + /* an address in the heap area, allocate all of the required heap */ + else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) { + block_addr = vm->heap_upper_limit; + block_nr_bytes = vm->heap_bound - vm->heap_upper_limit; + vm->heap_upper_limit = vm->heap_bound; + } + /* oops - an invalid address - abort the cpu */ + else if (processor != NULL) { + cpu_halt(processor, cia, was_signalled, SIGSEGV); + return 0; + } + /* 2*oops - an invalid address and no processor */ + else { + return 0; + } + + /* got the parameters, allocate the space */ + me->parent->callback->attach_address(me->parent, + "vm@0x0,0", /* stop remap */ + attach_raw_memory, + 0 /*address space*/, + block_addr, + block_nr_bytes, + access_read_write, + me); + return block_nr_bytes; +} + + +STATIC_INLINE_DEVICES unsigned +vm_io_read_buffer_callback(const device *me, + void *dest, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { + bzero(dest, nr_bytes); /* always initialized to zero */ + return nr_bytes; + } + else + return 0; +} + + +STATIC_INLINE_DEVICES unsigned +vm_io_write_buffer_callback(const device *me, + const void *source, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + cpu *processor, + unsigned_word cia) +{ + if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { + return me->parent->callback->dma_write_buffer(me->parent, source, + address_space, addr, + nr_bytes, + 0/*violate_read_only*/); + } + else + return 0; +} + + +STATIC_INLINE_DEVICES void +vm_ioctl_callback(const device *me, + psim *system, + cpu *processor, + unsigned_word cia, + ...) +{ + /* While the caller is notified that the heap has grown by the + requested amount, the heap is infact extended out to a page + boundary. */ + vm_device *vm = (vm_device*)me->data; + unsigned_word new_break = ALIGN_8(cpu_registers(processor)->gpr[3]); + unsigned_word old_break = vm->heap_bound; + signed_word delta = new_break - old_break; + if (delta > 0) + vm->heap_bound = ALIGN_PAGE(new_break); + cpu_registers(processor)->gpr[0] = 0; + cpu_registers(processor)->gpr[3] = new_break; +} + + +static device_callbacks const vm_callbacks = { + vm_init_callback, + vm_attach_address, + pass_device_detach_address, + vm_io_read_buffer_callback, + vm_io_write_buffer_callback, + unimp_device_dma_read_buffer, + pass_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + vm_ioctl_callback, +}; + + +STATIC_INLINE_DEVICES const device * +vea_vm_create(const char *name, + const device *parent) +{ + vm_device *vm = ZALLOC(vm_device); + unsigned_word addr; + unsigned nr_bytes; + + /* extract out the stack parameters */ + if (scand_uw_u(name, &addr, &nr_bytes) != 2) + error("vm_device_create() invalid vm device %s\n", name); + vm->stack_base = addr; + vm->stack_bound = addr + nr_bytes; + + /* insert in the tree including the buffer */ + return device_create_from(name, + vm, /* data */ + &vm_callbacks, + parent); +} + + + +/* Memory init device: memory@0x<addr>,<size>,<access> + + This strange device is used create sections of memory */ + +STATIC_INLINE_DEVICES void +memory_init_callback(const device *me, + psim *system) +{ + unsigned_word addr; + unsigned nr_bytes; + unsigned access; + + if (scand_uw_u_u(me->name, &addr, &nr_bytes, &access) != 3) + error("memory_init_callback() invalid memory device %s\n", me->name); + + me->parent->callback->attach_address(me->parent, + me->name, + attach_raw_memory, + 0 /*address space*/, + addr, + nr_bytes, + (access_type)access, + me); +} + + +static device_callbacks const memory_callbacks = { + memory_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + + +STATIC_INLINE_DEVICES const device * +memory_create(const char *name, + const device *parent) +{ + void *buffer; + unsigned_word addr; + unsigned nr_bytes; + if (scand_uw_u(name, &addr, &nr_bytes) != 2) + error("memory_create() invalid memory device %s\n"); + + /* insert in the tree including the buffer */ + return device_create_from(name, + buffer, /* data */ + &memory_callbacks, + parent); +} + + + +/* IOBUS device: iobus@<address> + + Simple bus on which some IO devices live */ + +STATIC_INLINE_DEVICES void +iobus_attach_address_callback(const device *me, + const char *name, + attach_type type, + int address_space, + unsigned_word addr, + unsigned nr_bytes, + access_type access, + const device *who) /*callback/default*/ +{ + unsigned_word iobus_addr; + /* sanity check */ + if (type == attach_default) + error("iobus_attach_address_callback() no default for %s/%s\n", + me->name, name); + if (address_space != 0) + error("iobus_attach_address_callback() no address_space for %s/%s\n", + me->name, name); + /* get the bus address */ + if (scand_uw(me->name, &iobus_addr) != 1) + error("iobus_attach_address_callback() invalid address for %s\n", + me->name); + me->parent->callback->attach_address(me->parent, + me->name, + type, + 0 /*address_space*/, + iobus_addr + addr, + nr_bytes, + access, + who); +} + + +STATIC_INLINE_DEVICES void +iobus_do_interrupt(event_queue *queue, + void *data) { cpu *target = (cpu*)data; /* try to interrupt the processor. If the attempt fails, try again on the next tick */ if (!external_interrupt(target)) - event_queue_schedule(queue, 1, icu_do_interrupt, target); + event_queue_schedule(queue, 1, iobus_do_interrupt, target); } + STATIC_INLINE_DEVICES void -icu_interrupt_callback(device_node *me, - int interrupt_status, - device_node *device, - cpu *processor, - unsigned_word cia) +iobus_interrupt_callback(const device *me, + const device *who, + int interrupt_line, + int interrupt_status, + cpu *processor, + unsigned_word cia) { /* the interrupt controler can't interrupt a cpu at any time. Rather it must synchronize with the system clock before @@ -313,130 +1019,605 @@ icu_interrupt_callback(device_node *me, cpu *target = psim_cpu(system, interrupt_status); if (target != NULL) { event_queue *events = cpu_event_queue(target); - event_queue_schedule(events, 1, icu_do_interrupt, target); + event_queue_schedule(events, 1, iobus_do_interrupt, target); } } -static device_callbacks icu_callbacks = { - icu_read_callback, - icu_write_callback, - icu_interrupt_callback, + +static device_callbacks const iobus_callbacks = { + ignore_device_init, + iobus_attach_address_callback, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + iobus_interrupt_callback, + unimp_device_interrupt_ack, + unimp_device_ioctl, }; -STATIC_INLINE_DEVICES device_node * -icu_create(device_node *parent, - char *name) + + +/* FILE device: file@0x<address>,<file-name> + (later - file@0x<address>,<size>,<file-offset>,<file-name>) + + Specifies a file to read directly into memory starting at <address> */ + + +STATIC_INLINE_DEVICES void +file_init_callback(const device *me, + psim *system) { - device_node *device; - unsigned address_base; - unsigned address_flags; + unsigned_word addr; + char *file_name; + char buf; + FILE *image; + + if ((file_name = strchr(me->name, ',')) == NULL + || scand_uw(me->name, &addr) != 1) + error("file_init_callback() invalid file device %s\n", me->name); + + /* open the file to load */ + file_name++; /* skip the `,' */ + image = fopen(file_name, "r"); + if (image == NULL) + error("file_init_callback() file open failed for %s\n", me->name); + + /* read it in slowly */ + while (fread(&buf, 1, 1, image) > 0) { + me->parent->callback->dma_write_buffer(me->parent, + &buf, + 0 /*address-space*/, + addr, + 1 /*nr-bytes*/, + 1 /*violate ro*/); + addr++; + } - /* extract the two arguments */ - parse_device_address(name, &address_base, &address_flags); + /* close down again */ + fclose(image); +} - /* insert into the device tree along with its address info */ - device = device_node_create(parent, name, sequential_device, - &icu_callbacks, 0); - device_node_add_address(device, - address_base, - 4, - device_is_read_write_exec, - NULL); - return device; +static device_callbacks const file_callbacks = { + file_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + + + +/* HTAB: htab@0x<address>,<nr_bytes> + PTE: pte@0x<effective-address>,0x<real-address>,<nr_bytes> + + HTAB defines the location (in physical memory) of a HASH table. + PTE (as a child of HTAB) defines a mapping that is to be entered + into that table. + + NB: All the work in this device is done during init by the PTE. + The pte, looks up its parent to determine the address of the HTAB + and then uses DMA calls to establish the required mapping. */ + + +STATIC_INLINE_DEVICES void +htab_init_callback(const device *me, + psim *system) +{ + /* only the pte does work */ + if (strncmp(me->name, "pte@", strlen("pte@")) == 0) { + unsigned_word htab_ra; + unsigned htab_nr_bytes; + unsigned_word pte_ea; + unsigned_word pte_ra; + unsigned pte_nr_bytes; + /* determine the location/size of the hash table */ + if (scand_uw_u(me->parent->name, &htab_ra, &htab_nr_bytes) != 2) + error("htab_init_callback() htab entry %s invalid\n", + me->parent->name); + /* determine the location/size of the mapping */ + if (scand_uw_uw_u(me->name, &pte_ea, &pte_ra, &pte_nr_bytes) != 3) + error("htab_init_callback() pte entry %s invalid\n", me->name); + error("Map ea=0x%x, ra=0x%x, nr_bytes=%d using htab=0x%x, nr_bytes=%d\n", + pte_ea, pte_ra, pte_nr_bytes, htab_ra, htab_nr_bytes); + } } -static device_descriptor icu_descriptor = { - "icu", - icu_create, + +static device_callbacks const htab_callbacks = { + htab_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, }; + +/* Simulator device: sim@0x<address>,<nr_bytes> + + Eventually gives access to the hardware configuration. For + instance, it could allow the setting (on the fly) of variables such + as hardware floating-point or strict-alignment. + + It's intended use is as part of testing the simulators + functionality */ + +static device_callbacks const sim_callbacks = { + ignore_device_init, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, +}; + +/* Load device: *binary@<file-name> -/* HALT device: + Assuming that <file-name> is an executable file understood by BFD, + this device loads or maps the relevant text/data segments into + memory using dma. */ - With real hardware, the processor operation is normally terminated - through a reset. This device illustrates how a reset device could - be attached to an address */ +/* create a device tree from the image */ -STATIC_INLINE_DEVICES unsigned64 -halt_read_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - cpu *processor, - unsigned_word cia) +STATIC_INLINE_DEVICES void +update_device_tree_for_section(bfd *abfd, + asection *the_section, + PTR obj) { - cpu_halt(processor, cia, was_exited, 0); - return 0; + unsigned_word section_vma; + unsigned_word section_size; + access_type access; + device *me = (device*)obj; + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, the_section) & SEC_ALLOC)) + return; + + /* check/ignore any sections of size zero */ + section_size = bfd_get_section_size_before_reloc(the_section); + if (section_size == 0) + return; + + /* find where it is to go */ + section_vma = bfd_get_section_vma(abfd, the_section); + + TRACE(trace_device_tree, + ("name=%-7s, vma=0x%.8x, size=%6d, flags=%3x(%s%s%s%s )\n", + bfd_get_section_name(abfd, the_section), + section_vma, section_size, + bfd_get_section_flags(abfd, the_section), + bfd_get_section_flags(abfd, the_section) & SEC_LOAD ? " LOAD" : "", + bfd_get_section_flags(abfd, the_section) & SEC_CODE ? " CODE" : "", + bfd_get_section_flags(abfd, the_section) & SEC_DATA ? " DATA" : "", + bfd_get_section_flags(abfd, the_section) & SEC_ALLOC ? " ALLOC" : "", + bfd_get_section_flags(abfd, the_section) & SEC_READONLY ? " READONLY" : "" + )); + + /* determine the devices access */ + access = access_read; + if (bfd_get_section_flags(abfd, the_section) & SEC_CODE) + access |= access_exec; + if (!(bfd_get_section_flags(abfd, the_section) & SEC_READONLY)) + access |= access_write; + + /* if a map, pass up a request to create the memory in core */ + if (strncmp(me->name, "map-binary@", strlen("map-binary@")) == 0) + me->parent->callback->attach_address(me->parent, + me->name, + attach_raw_memory, + 0 /*address space*/, + section_vma, + section_size, + access, + me); + + /* if a load dma in the required data */ + if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) { + void *section_init = zalloc(section_size); + if (!bfd_get_section_contents(abfd, + the_section, + section_init, 0, + section_size)) { + bfd_perror("core:load_section()"); + error("load of data failed"); + return; + } + if (me->parent->callback->dma_write_buffer(me->parent, + section_init, + 0 /*address_space*/, + section_vma, + section_size, + 1 /*violate_read_only*/) + != section_size) + error("data_init_callback() broken transfer for %s\n", me->name); + zfree(section_init); /* only free if load */ + } } + STATIC_INLINE_DEVICES void -halt_write_callback(device_node *device, - unsigned_word base, - unsigned nr_bytes, - unsigned64 val, - cpu *processor, - unsigned_word cia) +binary_init_callback(const device *me, + psim *system) { - cpu_halt(processor, cia, was_exited, 0); + char file_name[100]; + bfd *image; + + /* get a file name */ + if (scand_c(me->name, file_name) != 1) + error("load_binary_init_callback() invalid load-binary device %s\n", + me->name); + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror("open failed:"); + error("nothing loaded\n"); + } + + /* check it is valid */ + if (!bfd_check_format(image, bfd_object)) { + printf_filtered("create_device_tree() - FIXME - should check more bfd bits\n"); + printf_filtered("create_device_tree() - %s not an executable, assume device file\n", file_name); + bfd_close(image); + image = NULL; + } + + /* and the data sections */ + bfd_map_over_sections(image, + update_device_tree_for_section, + (PTR)me); + + bfd_close(image); } -static device_callbacks halt_callbacks = { - halt_read_callback, - halt_write_callback, +static device_callbacks const binary_callbacks = { + binary_init_callback, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, }; -STATIC_INLINE_DEVICES device_node * -halt_create(device_node *parent, - char *name) + + +/* Stack device: stack@<type> + + Has a single IOCTL to create a stack frame of the specified type. + If <type> is elf or xcoff then a corresponding stack is created. + Any other value of type is ignored. + + The IOCTL takes the additional arguments: + + unsigned_word stack_end -- where the stack should come down from + char **argv -- ... + char **envp -- ... + + */ + +STATIC_INLINE_DEVICES int +sizeof_argument_strings(char **arg) +{ + int sizeof_strings = 0; + + /* robust */ + if (arg == NULL) + return 0; + + /* add up all the string sizes (padding as we go) */ + for (; *arg != NULL; arg++) { + int len = strlen(*arg) + 1; + sizeof_strings += ALIGN_8(len); + } + + return sizeof_strings; +} + +STATIC_INLINE_DEVICES int +number_of_arguments(char **arg) +{ + int nr; + if (arg == NULL) + return 0; + for (nr = 0; *arg != NULL; arg++, nr++); + return nr; +} + +STATIC_INLINE_DEVICES int +sizeof_arguments(char **arg) +{ + return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word)); +} + +STATIC_INLINE_DEVICES void +write_stack_arguments(psim *system, + char **arg, + unsigned_word start_block, + unsigned_word end_block, + unsigned_word start_arg, + unsigned_word end_arg) +{ + TRACE(trace_create_stack, + ("write_stack_arguments() - %s=0x%x %s=0x%x %s=0x%x %s=0x%x\n", + "system", system, "arg", arg, + "start_block", start_block, "start_arg", start_arg)); + if (arg == NULL) + error("write_arguments: character array NULL\n"); + /* only copy in arguments, memory is already zero */ + for (; *arg != NULL; arg++) { + int len = strlen(*arg)+1; + unsigned_word target_start_block; + TRACE(trace_create_stack, + ("write_stack_arguments - write %s=%s at %s=0x%x %s=0x%x %s=0x%x\n", + "**arg", *arg, "start_block", start_block, + "len", len, "start_arg", start_arg)); + if (psim_write_memory(system, 0, *arg, + start_block, len, + 0/*violate_readonly*/) != len) + error("write_stack_arguments() - write of **arg (%s) at 0x%x failed\n", + *arg, start_block); + target_start_block = H2T_word(start_block); + if (psim_write_memory(system, 0, &target_start_block, + start_arg, sizeof(target_start_block), + 0) != sizeof(target_start_block)) + error("write_stack_arguments() - write of *arg failed\n"); + start_block += ALIGN_8(len); + start_arg += sizeof(start_block); + } + start_arg += sizeof(start_block); /*the null at the end*/ + if (start_block != end_block + || ALIGN_8(start_arg) != end_arg) + error("write_stack_arguments - possible corruption\n"); +} + +STATIC_INLINE_DEVICES void +create_elf_stack_frame(psim *system, + unsigned_word bottom_of_stack, + char **argv, + char **envp) +{ + /* fixme - this is over aligned */ + + /* information block */ + const unsigned sizeof_envp_block = sizeof_argument_strings(envp); + const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block; + const unsigned sizeof_argv_block = sizeof_argument_strings(argv); + const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block; + + /* auxiliary vector - contains only one entry */ + const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */ + const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry); + + /* environment points (including null sentinal) */ + const unsigned sizeof_envp = sizeof_arguments(envp); + const unsigned_word start_envp = start_aux - sizeof_envp; + + /* argument pointers (including null sentinal) */ + const int argc = number_of_arguments(argv); + const unsigned sizeof_argv = sizeof_arguments(argv); + const unsigned_word start_argv = start_envp - sizeof_argv; + + /* link register save address - alligned to a 16byte boundary */ + const unsigned_word top_of_stack = ((start_argv + - 2 * sizeof(unsigned_word)) + & ~0xf); + + /* install arguments on stack */ + write_stack_arguments(system, envp, + start_envp_block, bottom_of_stack, + start_envp, start_aux); + write_stack_arguments(system, argv, + start_argv_block, start_envp_block, + start_argv, start_envp); + + /* set up the registers */ + psim_write_register(system, -1, + &top_of_stack, "sp", cooked_transfer); + psim_write_register(system, -1, + &argc, "r3", cooked_transfer); + psim_write_register(system, -1, + &start_argv, "r4", cooked_transfer); + psim_write_register(system, -1, + &start_envp, "r5", cooked_transfer); + psim_write_register(system, -1, + &start_aux, "r6", cooked_transfer); +} + +STATIC_INLINE_DEVICES void +create_aix_stack_frame(psim *system, + unsigned_word bottom_of_stack, + char **argv, + char **envp) { - device_node *device; - unsigned address_base; - unsigned address_flags; + unsigned_word core_envp; + unsigned_word core_argv; + unsigned_word core_argc; + unsigned_word core_aux; + unsigned_word top_of_stack; + + /* cheat - create an elf stack frame */ + create_elf_stack_frame(system, bottom_of_stack, argv, envp); + + /* extract argument addresses from registers */ + psim_read_register(system, 0, &top_of_stack, "r1", cooked_transfer); + psim_read_register(system, 0, &core_argc, "r3", cooked_transfer); + psim_read_register(system, 0, &core_argv, "r4", cooked_transfer); + psim_read_register(system, 0, &core_envp, "r5", cooked_transfer); + psim_read_register(system, 0, &core_aux, "r6", cooked_transfer); + + /* extract arguments from registers */ + error("create_aix_stack_frame() - what happens next?\n"); +} + + - parse_device_address(name, &address_base, &address_flags); - device = device_node_create(parent, name, other_device, - &halt_callbacks, NULL); - device_node_add_address(device, - address_base, - 4, - device_is_read_write_exec, - NULL); - return device; +STATIC_INLINE_DEVICES void +stack_ioctl_callback(const device *me, + psim *system, + cpu *processor, + unsigned_word cia, + ...) +{ + va_list ap; + unsigned_word stack_pointer; + char **argv; + char **envp; + va_start(ap, cia); + stack_pointer = va_arg(ap, unsigned_word); + argv = va_arg(ap, char **); + envp = va_arg(ap, char **); + if (strcmp(me->name, "stack@elf") == 0) + create_elf_stack_frame(system, stack_pointer, argv, envp); + else if (strcmp(me->name, "stack@xcoff") == 0) + create_aix_stack_frame(system, stack_pointer, argv, envp); } -static device_descriptor halt_descriptor = { - "halt", - halt_create, + +static device_callbacks const stack_callbacks = { + ignore_device_init, + unimp_device_attach_address, + unimp_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + unimp_device_dma_read_buffer, + unimp_device_dma_write_buffer, + unimp_device_attach_interrupt, + unimp_device_detach_interrupt, + unimp_device_interrupt, + unimp_device_interrupt_ack, + stack_ioctl_callback, }; -static device_descriptor *devices[] = { - &console_descriptor, - &halt_descriptor, - &icu_descriptor, - NULL, + +/* Table of all the devices and a function to lookup/create a device + from its name */ + +typedef const device *(device_creator) + (const char *name, + const device *parent); + +typedef struct _device_descriptor device_descriptor; +struct _device_descriptor { + const char *name; + device_creator *creator; + const device_callbacks *callbacks; +}; + +static device_descriptor devices[] = { + { "console", console_create, NULL }, + { "memory", memory_create, NULL }, + { "vm", vea_vm_create, NULL }, + { "halt", NULL, &halt_callbacks }, + { "icu", NULL, &icu_callbacks }, + { "register", NULL, ®ister_callbacks }, + { "iobus", NULL, &iobus_callbacks }, + { "file", NULL, &file_callbacks }, + { "htab", NULL, &htab_callbacks }, + { "pte", NULL, &htab_callbacks }, /* yep - uses htab's table */ + { "stack", NULL, &stack_callbacks }, + { "sim", NULL, &sim_callbacks }, + { "load-binary", NULL, &binary_callbacks }, + { "map-binary", NULL, &binary_callbacks }, + { NULL }, }; -INLINE_DEVICES device_descriptor * -find_device_descriptor(char *name) +INLINE_DEVICES const device * +device_create(const char *name, + const device *parent) { - device_descriptor **device; + device_descriptor *device; int name_len; char *chp; chp = strchr(name, '@'); name_len = (chp == NULL ? strlen(name) : chp - name); - for (device = devices; *device != NULL; device++) { - if (strncmp(name, (*device)->name, name_len) == 0 - && ((*device)->name[name_len] == '\0' - || (*device)->name[name_len] == '@')) - return *device; + for (device = devices; device->name != NULL; device++) { + if (strncmp(name, device->name, name_len) == 0 + && (device->name[name_len] == '\0' + || device->name[name_len] == '@')) + if (device->creator != NULL) + return device->creator(name, parent); + else + return device_create_from(name, + NULL /* data */, + device->callbacks, + parent); } + error("device_create() unknown device %s\n", name); return NULL; } + +INLINE_DEVICES const device * +device_create_from(const char *name, + void *data, + const device_callbacks *callback, + const device *parent) +{ + device *me = ZALLOC(device); + me->name = strdup(name); + me->data = data; + me->callback = callback; + me->parent = parent; + return me; +} + + +INLINE_DEVICES const device_callbacks * +passthrough_device_callbacks(void) +{ + static const device_callbacks callbacks = { + ignore_device_init, + pass_device_attach_address, + pass_device_detach_address, + unimp_device_io_read_buffer, + unimp_device_io_write_buffer, + pass_device_dma_read_buffer, + pass_device_dma_write_buffer, + pass_device_attach_interrupt, + pass_device_detach_interrupt, + pass_device_interrupt, + unimp_device_interrupt_ack, + unimp_device_ioctl, + }; + return &callbacks; +} + + + #endif /* _DEVICES_C_ */ |