summaryrefslogtreecommitdiff
path: root/sim/ppc/hw_vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/ppc/hw_vm.c')
-rw-r--r--sim/ppc/hw_vm.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/sim/ppc/hw_vm.c b/sim/ppc/hw_vm.c
new file mode 100644
index 00000000000..e42b8002347
--- /dev/null
+++ b/sim/ppc/hw_vm.c
@@ -0,0 +1,275 @@
+/* This file is part of the program psim.
+
+ Copyright (C) 1994-1996, 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 _HW_VM_C_
+#define _HW_VM_C_
+
+#include "device_table.h"
+#include "cpu.h"
+
+#include <signal.h>
+
+/* DEVICE
+
+ vm - virtual memory device for user simulation modes
+
+ DESCRIPTION
+
+ In user mode, mapped text, data and stack addresses are managed by
+ the core. Unmapped addresses are passed onto this device (because
+ it establishes its self as the fallback device) for processing.
+
+ During initialization, children of this device will request the
+ mapping of the initial text and data segments. Those requests are
+ passed onto the core device so that that may establish the initial
+ memory regions.
+
+ Once the simulation has started (as noted above) any access to an
+ unmapped address range will be passed down to this device as an IO
+ access. This device will then either attach additional memory to
+ the core device or signal the access as being invalid.
+
+ The IOCTL function is used to notify this device of any changes to
+ the users `brk' point.
+
+ PROPERTIES
+
+ stack-base = <number>
+
+ Specifies the lower address of the stack segment in the users
+ virtual address space. The initial stack page is defined by
+ stack-base + nr-bytes.
+
+ nr-bytes = <number>
+
+ Specifies the maximum size of the stack segment in the users
+ address space.
+
+ */
+
+typedef struct _hw_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;
+} hw_vm_device;
+
+
+static void
+hw_vm_init_address_callback(device *me)
+{
+ hw_vm_device *vm = (hw_vm_device*)device_data(me);
+
+ /* revert the stack/heap variables to their defaults */
+ vm->stack_base = device_find_integer_property(me, "stack-base");
+ vm->stack_bound = (vm->stack_base
+ + device_find_integer_property(me, "nr-bytes"));
+ 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 */
+ device_attach_address(device_parent(me),
+ attach_callback + 1,
+ 0 /*address space - ignore*/,
+ 0 /*addr - ignore*/,
+ (((unsigned)0)-1) /*nr_bytes - ignore*/,
+ access_read_write /*access*/,
+ me);
+}
+
+
+static void
+hw_vm_attach_address(device *me,
+ attach_type attach,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ access_type access,
+ device *client) /*callback/default*/
+{
+ hw_vm_device *vm = (hw_vm_device*)device_data(me);
+ /* 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;
+ }
+ device_attach_address(device_parent(me),
+ attach_raw_memory,
+ 0 /*address space*/,
+ addr,
+ nr_bytes,
+ access,
+ me);
+}
+
+
+static unsigned
+hw_vm_add_space(device *me,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ hw_vm_device *vm = (hw_vm_device*)device_data(me);
+ 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 */
+ device_attach_address(device_parent(me),
+ attach_raw_memory,
+ 0 /*address space*/,
+ block_addr,
+ block_nr_bytes,
+ access_read_write,
+ me);
+ return block_nr_bytes;
+}
+
+
+static unsigned
+hw_vm_io_read_buffer_callback(device *me,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) {
+ memset(dest, 0, nr_bytes); /* always initialized to zero */
+ return nr_bytes;
+ }
+ else
+ return 0;
+}
+
+
+static unsigned
+hw_vm_io_write_buffer_callback(device *me,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) {
+ return device_dma_write_buffer(device_parent(me), source,
+ space, addr,
+ nr_bytes,
+ 0/*violate_read_only*/);
+ }
+ else
+ return 0;
+}
+
+
+static int
+hw_vm_ioctl(device *me,
+ cpu *processor,
+ unsigned_word cia,
+ device_ioctl_request request,
+ va_list ap)
+{
+ /* While the caller is notified that the heap has grown by the
+ requested amount, the heap is actually extended out to a page
+ boundary. */
+ hw_vm_device *vm = (hw_vm_device*)device_data(me);
+ switch (request) {
+ case device_ioctl_break:
+ {
+ unsigned_word requested_break = va_arg(ap, unsigned_word);
+ unsigned_word new_break = ALIGN_8(requested_break);
+ unsigned_word old_break = vm->heap_bound;
+ signed_word delta = new_break - old_break;
+ if (delta > 0)
+ vm->heap_bound = ALIGN_PAGE(new_break);
+ break;
+ }
+ default:
+ device_error(me, "Unsupported ioctl request");
+ break;
+ }
+ return 0;
+
+}
+
+
+static device_callbacks const hw_vm_callbacks = {
+ { hw_vm_init_address_callback, },
+ { hw_vm_attach_address,
+ passthrough_device_address_detach, },
+ { hw_vm_io_read_buffer_callback,
+ hw_vm_io_write_buffer_callback, },
+ { NULL, passthrough_device_dma_write_buffer, },
+ { NULL, }, /* interrupt */
+ { generic_device_unit_decode,
+ generic_device_unit_encode, },
+ NULL, /* instance */
+ hw_vm_ioctl,
+};
+
+
+static void *
+hw_vm_create(const char *name,
+ const device_unit *address,
+ const char *args)
+{
+ hw_vm_device *vm = ZALLOC(hw_vm_device);
+ return vm;
+}
+
+const device_descriptor hw_vm_device_descriptor[] = {
+ { "vm", hw_vm_create, &hw_vm_callbacks },
+ { NULL },
+};
+
+#endif _HW_VM_C_