Commit | Line | Data |
---|---|---|
8477437c MM |
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 | device_name(me), | |
97 | attach_callback + 1, | |
98 | 0 /*address space - ignore*/, | |
99 | 0 /*addr - ignore*/, | |
100 | (((unsigned)0)-1) /*nr_bytes - ignore*/, | |
101 | access_read_write /*access*/, | |
102 | me); | |
103 | } | |
104 | ||
105 | ||
106 | static void | |
107 | hw_vm_attach_address(device *me, | |
108 | const char *name, | |
109 | attach_type attach, | |
110 | int space, | |
111 | unsigned_word addr, | |
112 | unsigned nr_bytes, | |
113 | access_type access, | |
114 | device *who) /*callback/default*/ | |
115 | { | |
116 | hw_vm_device *vm = (hw_vm_device*)device_data(me); | |
117 | /* update end of bss if necessary */ | |
118 | if (vm->heap_base < addr + nr_bytes) { | |
119 | vm->heap_base = addr + nr_bytes; | |
120 | vm->heap_bound = addr + nr_bytes; | |
121 | vm->heap_upper_limit = addr + nr_bytes; | |
122 | } | |
123 | device_attach_address(device_parent(me), | |
124 | device_name(me), | |
125 | attach_raw_memory, | |
126 | 0 /*address space*/, | |
127 | addr, | |
128 | nr_bytes, | |
129 | access, | |
130 | me); | |
131 | } | |
132 | ||
133 | ||
134 | static unsigned | |
135 | hw_vm_add_space(device *me, | |
136 | unsigned_word addr, | |
137 | unsigned nr_bytes, | |
138 | cpu *processor, | |
139 | unsigned_word cia) | |
140 | { | |
141 | hw_vm_device *vm = (hw_vm_device*)device_data(me); | |
142 | unsigned_word block_addr; | |
143 | unsigned block_nr_bytes; | |
144 | ||
145 | /* an address in the stack area, allocate just down to the addressed | |
146 | page */ | |
147 | if (addr >= vm->stack_base && addr < vm->stack_lower_limit) { | |
148 | block_addr = FLOOR_PAGE(addr); | |
149 | block_nr_bytes = vm->stack_lower_limit - block_addr; | |
150 | vm->stack_lower_limit = block_addr; | |
151 | } | |
152 | /* an address in the heap area, allocate all of the required heap */ | |
153 | else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) { | |
154 | block_addr = vm->heap_upper_limit; | |
155 | block_nr_bytes = vm->heap_bound - vm->heap_upper_limit; | |
156 | vm->heap_upper_limit = vm->heap_bound; | |
157 | } | |
158 | /* oops - an invalid address - abort the cpu */ | |
159 | else if (processor != NULL) { | |
160 | cpu_halt(processor, cia, was_signalled, SIGSEGV); | |
161 | return 0; | |
162 | } | |
163 | /* 2*oops - an invalid address and no processor */ | |
164 | else { | |
165 | return 0; | |
166 | } | |
167 | ||
168 | /* got the parameters, allocate the space */ | |
169 | device_attach_address(device_parent(me), | |
170 | "vm@0x0,0", /* stop remap */ | |
171 | attach_raw_memory, | |
172 | 0 /*address space*/, | |
173 | block_addr, | |
174 | block_nr_bytes, | |
175 | access_read_write, | |
176 | me); | |
177 | return block_nr_bytes; | |
178 | } | |
179 | ||
180 | ||
181 | static unsigned | |
182 | hw_vm_io_read_buffer_callback(device *me, | |
183 | void *dest, | |
184 | int space, | |
185 | unsigned_word addr, | |
186 | unsigned nr_bytes, | |
187 | cpu *processor, | |
188 | unsigned_word cia) | |
189 | { | |
190 | if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { | |
191 | memset(dest, 0, nr_bytes); /* always initialized to zero */ | |
192 | return nr_bytes; | |
193 | } | |
194 | else | |
195 | return 0; | |
196 | } | |
197 | ||
198 | ||
199 | static unsigned | |
200 | hw_vm_io_write_buffer_callback(device *me, | |
201 | const void *source, | |
202 | int space, | |
203 | unsigned_word addr, | |
204 | unsigned nr_bytes, | |
205 | cpu *processor, | |
206 | unsigned_word cia) | |
207 | { | |
208 | if (hw_vm_add_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { | |
209 | return device_dma_write_buffer(device_parent(me), source, | |
210 | space, addr, | |
211 | nr_bytes, | |
212 | 0/*violate_read_only*/); | |
213 | } | |
214 | else | |
215 | return 0; | |
216 | } | |
217 | ||
218 | ||
219 | static int | |
220 | hw_vm_ioctl_callback(device *me, | |
221 | cpu *processor, | |
222 | unsigned_word cia, | |
223 | va_list ap) | |
224 | { | |
225 | /* While the caller is notified that the heap has grown by the | |
226 | requested amount, the heap is actually extended out to a page | |
227 | boundary. */ | |
228 | hw_vm_device *vm = (hw_vm_device*)device_data(me); | |
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 | return 0; | |
236 | } | |
237 | ||
238 | ||
239 | static device_callbacks const hw_vm_callbacks = { | |
240 | { hw_vm_init_address_callback, }, | |
241 | { hw_vm_attach_address, | |
242 | passthrough_device_address_detach, }, | |
243 | { hw_vm_io_read_buffer_callback, | |
244 | hw_vm_io_write_buffer_callback, }, | |
245 | { NULL, passthrough_device_dma_write_buffer, }, | |
246 | { NULL, }, /* interrupt */ | |
247 | { generic_device_unit_decode, | |
248 | generic_device_unit_encode, }, | |
249 | NULL, /* instance */ | |
250 | hw_vm_ioctl_callback, | |
251 | }; | |
252 | ||
253 | ||
254 | static void * | |
255 | hw_vm_create(const char *name, | |
256 | const device_unit *address, | |
257 | const char *args, | |
258 | device *parent) | |
259 | { | |
260 | hw_vm_device *vm = ZALLOC(hw_vm_device); | |
261 | return vm; | |
262 | } | |
263 | ||
264 | const device_descriptor hw_vm_device_descriptor[] = { | |
265 | { "vm", hw_vm_create, &hw_vm_callbacks }, | |
266 | { NULL }, | |
267 | }; | |
268 | ||
269 | #endif _HW_VM_C_ |