1/*  This file is part of the program psim.
2
3    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19    */
20
21
22#ifndef _HW_VM_C_
23#define _HW_VM_C_
24
25#include "device_table.h"
26#include "cpu.h"
27
28#include <signal.h>
29
30/* DEVICE
31
32   vm - virtual memory device for user simulation modes
33
34   DESCRIPTION
35
36   In user mode, mapped text, data and stack addresses are managed by
37   the core.  Unmapped addresses are passed onto this device (because
38   it establishes its self as the fallback device) for processing.
39
40   During initialization, children of this device will request the
41   mapping of the initial text and data segments.  Those requests are
42   passed onto the core device so that that may establish the initial
43   memory regions.
44
45   Once the simulation has started (as noted above) any access to an
46   unmapped address range will be passed down to this device as an IO
47   access.  This device will then either attach additional memory to
48   the core device or signal the access as being invalid.
49
50   The IOCTL function is used to notify this device of any changes to
51   the users `brk' point.
52
53   PROPERTIES
54
55   stack-base = <number>
56
57   Specifies the lower address of the stack segment in the users
58   virtual address space.  The initial stack page is defined by
59   stack-base + nr-bytes.
60
61   nr-bytes = <number>
62
63   Specifies the maximum size of the stack segment in the users
64   address space.
65
66   */
67
68typedef struct _hw_vm_device {
69  /* area of memory valid for stack addresses */
70  unsigned_word stack_base; /* min possible stack value */
71  unsigned_word stack_bound;
72  unsigned_word stack_lower_limit;
73  /* area of memory valid for heap addresses */
74  unsigned_word heap_base;
75  unsigned_word heap_bound;
76  unsigned_word heap_upper_limit;
77} hw_vm_device;
78
79
80static void
81hw_vm_init_address_callback(device *me)
82{
83  hw_vm_device *vm = (hw_vm_device*)device_data(me);
84
85  /* revert the stack/heap variables to their defaults */
86  vm->stack_base = device_find_integer_property(me, "stack-base");
87  vm->stack_bound = (vm->stack_base
88		     + device_find_integer_property(me, "nr-bytes"));
89  vm->stack_lower_limit = vm->stack_bound;
90  vm->heap_base = 0;
91  vm->heap_bound = 0;
92  vm->heap_upper_limit = 0;
93
94  /* establish this device as the default memory handler */
95  device_attach_address(device_parent(me),
96			attach_callback + 1,
97			0 /*address space - ignore*/,
98			0 /*addr - ignore*/,
99			(((unsigned)0)-1) /*nr_bytes - ignore*/,
100			access_read_write /*access*/,
101			me);
102}
103
104
105static void
106hw_vm_attach_address(device *me,
107		     attach_type attach,
108		     int space,
109		     unsigned_word addr,
110		     unsigned nr_bytes,
111		     access_type access,
112		     device *client) /*callback/default*/
113{
114  hw_vm_device *vm = (hw_vm_device*)device_data(me);
115  /* update end of bss if necessary */
116  if (vm->heap_base < addr + nr_bytes) {
117    vm->heap_base = addr + nr_bytes;
118    vm->heap_bound = addr + nr_bytes;
119    vm->heap_upper_limit = addr + nr_bytes;
120  }
121  device_attach_address(device_parent(me),
122			attach_raw_memory,
123			0 /*address space*/,
124			addr,
125			nr_bytes,
126			access,
127			me);
128}
129
130
131static unsigned
132hw_vm_add_space(device *me,
133		unsigned_word addr,
134		unsigned nr_bytes,
135		cpu *processor,
136		unsigned_word cia)
137{
138  hw_vm_device *vm = (hw_vm_device*)device_data(me);
139  unsigned_word block_addr;
140  unsigned block_nr_bytes;
141
142  /* an address in the stack area, allocate just down to the addressed
143     page */
144  if (addr >= vm->stack_base && addr < vm->stack_lower_limit) {
145    block_addr = FLOOR_PAGE(addr);
146    block_nr_bytes = vm->stack_lower_limit - block_addr;
147    vm->stack_lower_limit = block_addr;
148  }
149  /* an address in the heap area, allocate all of the required heap */
150  else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) {
151    block_addr = vm->heap_upper_limit;
152    block_nr_bytes = vm->heap_bound - vm->heap_upper_limit;
153    vm->heap_upper_limit = vm->heap_bound;
154  }
155  /* oops - an invalid address - abort the cpu */
156  else if (processor != NULL) {
157    cpu_halt(processor, cia, was_signalled, SIGSEGV);
158    return 0;
159  }
160  /* 2*oops - an invalid address and no processor */
161  else {
162    return 0;
163  }
164
165  /* got the parameters, allocate the space */
166  device_attach_address(device_parent(me),
167			attach_raw_memory,
168			0 /*address space*/,
169			block_addr,
170			block_nr_bytes,
171			access_read_write,
172			me);
173  return block_nr_bytes;
174}
175
176
177static unsigned
178hw_vm_io_read_buffer_callback(device *me,
179			   void *dest,
180			   int space,
181			   unsigned_word addr,
182			   unsigned nr_bytes,
183			   cpu *processor,
184			   unsigned_word cia)
185{
186  if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) {
187    memset(dest, 0, nr_bytes); /* always initialized to zero */
188    return nr_bytes;
189  }
190  else
191    return 0;
192}
193
194
195static unsigned
196hw_vm_io_write_buffer_callback(device *me,
197			    const void *source,
198			    int space,
199			    unsigned_word addr,
200			    unsigned nr_bytes,
201			    cpu *processor,
202			    unsigned_word cia)
203{
204  if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) {
205    return device_dma_write_buffer(device_parent(me), source,
206				   space, addr,
207				   nr_bytes,
208				   0/*violate_read_only*/);
209  }
210  else
211    return 0;
212}
213
214
215static int
216hw_vm_ioctl(device *me,
217	    cpu *processor,
218	    unsigned_word cia,
219	    device_ioctl_request request,
220	    va_list ap)
221{
222  /* While the caller is notified that the heap has grown by the
223     requested amount, the heap is actually extended out to a page
224     boundary. */
225  hw_vm_device *vm = (hw_vm_device*)device_data(me);
226  switch (request) {
227  case device_ioctl_break:
228    {
229      unsigned_word requested_break = va_arg(ap, unsigned_word);
230      unsigned_word new_break = ALIGN_8(requested_break);
231      unsigned_word old_break = vm->heap_bound;
232      signed_word delta = new_break - old_break;
233      if (delta > 0)
234	vm->heap_bound = ALIGN_PAGE(new_break);
235      break;
236    }
237  default:
238    device_error(me, "Unsupported ioctl request");
239    break;
240  }
241  return 0;
242
243}
244
245
246static device_callbacks const hw_vm_callbacks = {
247  { hw_vm_init_address_callback, },
248  { hw_vm_attach_address,
249    passthrough_device_address_detach, },
250  { hw_vm_io_read_buffer_callback,
251    hw_vm_io_write_buffer_callback, },
252  { NULL, passthrough_device_dma_write_buffer, },
253  { NULL, }, /* interrupt */
254  { generic_device_unit_decode,
255    generic_device_unit_encode, },
256  NULL, /* instance */
257  hw_vm_ioctl,
258};
259
260
261static void *
262hw_vm_create(const char *name,
263	     const device_unit *address,
264	     const char *args)
265{
266  hw_vm_device *vm = ZALLOC(hw_vm_device);
267  return vm;
268}
269
270const device_descriptor hw_vm_device_descriptor[] = {
271  { "vm", hw_vm_create, &hw_vm_callbacks },
272  { NULL },
273};
274
275#endif /* _HW_VM_C_ */
276