274 lines
7.2 KiB
C
274 lines
7.2 KiB
C
/* 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 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 _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_ */
|