Commit | Line | Data |
---|---|---|
c906108c SS |
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 | ||
68 | typedef 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 | ||
80 | static void | |
81 | hw_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 | ||
105 | static void | |
106 | hw_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 | ||
131 | static unsigned | |
132 | hw_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 | ||
177 | static unsigned | |
178 | hw_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 | ||
195 | static unsigned | |
196 | hw_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 | ||
215 | static int | |
216 | hw_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 | ||
246 | static 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 | ||
261 | static void * | |
262 | hw_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 | ||
270 | const device_descriptor hw_vm_device_descriptor[] = { | |
271 | { "vm", hw_vm_create, &hw_vm_callbacks }, | |
272 | { NULL }, | |
273 | }; | |
274 | ||
275 | #endif _HW_VM_C_ |