#ifndef _HW_VM_C_
#define _HW_VM_C_
#include "device_table.h"
#include "cpu.h"
#include <signal.h>
typedef struct _hw_vm_device {
unsigned_word stack_base;
unsigned_word stack_bound;
unsigned_word stack_lower_limit;
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);
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;
device_attach_address(device_parent(me),
attach_callback + 1,
0 ,
0 ,
(((unsigned)0)-1) ,
access_read_write ,
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)
{
hw_vm_device *vm = (hw_vm_device*)device_data(me);
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 ,
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;
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;
}
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;
}
else if (processor != NULL) {
cpu_halt(processor, cia, was_signalled, SIGSEGV);
return 0;
}
else {
return 0;
}
device_attach_address(device_parent(me),
attach_raw_memory,
0 ,
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);
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);
}
else
return 0;
}
static int
hw_vm_ioctl(device *me,
cpu *processor,
unsigned_word cia,
device_ioctl_request request,
va_list ap)
{
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, },
{ generic_device_unit_decode,
generic_device_unit_encode, },
NULL,
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_