Commit | Line | Data |
---|---|---|
30c87b55 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_MEMORY_C_ | |
23 | #define _HW_MEMORY_C_ | |
24 | ||
25 | #ifndef STATIC_INLINE_HW_MEMORY | |
26 | #define STATIC_INLINE_HW_MEMORY STATIC_INLINE | |
27 | #endif | |
28 | ||
29 | #include "device_table.h" | |
30 | ||
31 | /* DEVICE | |
32 | ||
33 | memory - description of system memory | |
34 | ||
35 | DESCRIPTION | |
36 | ||
37 | This device describes the size and location of the banks of | |
38 | physical memory within the simulation. | |
39 | ||
40 | In addition, this device supports the "claim" and "release" methods | |
41 | that can be used by OpenBoot client programs to manage the | |
42 | allocation of physical memory. | |
43 | ||
44 | PROPERTIES | |
45 | ||
46 | reg = { <address> <size> } (required) | |
47 | ||
48 | Each pair specify one bank of memory. | |
49 | ||
50 | available = { <address> <size> } (automatic) | |
51 | ||
52 | Each pair specifies a block of memory that is currently unallocated. | |
53 | ||
54 | */ | |
55 | ||
56 | typedef struct _memory_reg_spec { | |
57 | unsigned32 base; | |
58 | unsigned32 size; | |
59 | } memory_reg_spec; | |
60 | ||
61 | typedef struct _hw_memory_chunk hw_memory_chunk; | |
62 | struct _hw_memory_chunk { | |
63 | unsigned_word address; | |
64 | unsigned_word size; | |
65 | unsigned_word alloc_address; | |
66 | unsigned_word alloc_size; | |
67 | int available; | |
68 | hw_memory_chunk *next; | |
69 | }; | |
70 | ||
71 | typedef struct _hw_memory_device { | |
72 | hw_memory_chunk *heap; | |
73 | } hw_memory_device; | |
74 | ||
75 | ||
76 | static void * | |
77 | hw_memory_create(const char *name, | |
78 | const device_unit *unit_address, | |
79 | const char *args) | |
80 | { | |
81 | hw_memory_device *hw_memory = ZALLOC(hw_memory_device); | |
82 | return hw_memory; | |
83 | } | |
84 | ||
85 | ||
86 | static void | |
87 | hw_memory_set_available(device *me, | |
88 | hw_memory_device *hw_memory) | |
89 | { | |
90 | hw_memory_chunk *chunk = NULL; | |
91 | memory_reg_spec *available = NULL; | |
92 | int nr_available = 0; | |
93 | int curr = 0; | |
94 | int sizeof_available = 0; | |
95 | /* determine the nr of available chunks */ | |
96 | chunk = hw_memory->heap; | |
97 | nr_available = 0; | |
98 | while (chunk != NULL) { | |
99 | if (chunk->available) | |
100 | nr_available += 1; | |
101 | chunk = chunk->next; | |
102 | } | |
103 | /* now create the available struct */ | |
104 | ASSERT(nr_available > 0); | |
105 | sizeof_available = sizeof(memory_reg_spec) * nr_available; | |
106 | available = zalloc(sizeof_available); | |
107 | chunk = hw_memory->heap; | |
108 | curr = 0; | |
109 | while (chunk != NULL) { | |
110 | if (chunk->available) { | |
111 | available[curr].base = H2BE_4(chunk->address); | |
112 | available[curr].size = H2BE_4(chunk->size); | |
113 | curr += 1; | |
114 | } | |
115 | chunk = chunk->next; | |
116 | } | |
117 | /* update */ | |
118 | device_set_array_property(me, "available", available, sizeof_available); | |
119 | zfree(available); | |
120 | } | |
121 | ||
122 | ||
123 | static void | |
124 | hw_memory_init_address(device *me) | |
125 | { | |
126 | hw_memory_device *hw_memory = (hw_memory_device*)device_data(me); | |
127 | const device_property *reg = device_find_array_property(me, "reg"); | |
128 | const memory_reg_spec *spec = reg->array; | |
129 | int nr_entries = reg->sizeof_array / sizeof(*spec); | |
130 | ||
131 | /* sanity check reg property */ | |
132 | if ((reg->sizeof_array % sizeof(*spec)) != 0) | |
133 | error("devices/%s reg property of incorrect size\n", device_name(me)); | |
134 | ||
135 | /* free up any previous structures */ | |
136 | { | |
137 | hw_memory_chunk *curr_chunk = hw_memory->heap; | |
138 | hw_memory->heap = NULL; | |
139 | while (curr_chunk != NULL) { | |
140 | hw_memory_chunk *dead_chunk = curr_chunk; | |
141 | curr_chunk = dead_chunk->next; | |
142 | dead_chunk->next = NULL; | |
143 | zfree(dead_chunk); | |
144 | } | |
145 | } | |
146 | ||
147 | /* count/allocate memory entries */ | |
148 | { | |
149 | hw_memory_chunk **curr_chunk = &hw_memory->heap; | |
150 | while (nr_entries > 0) { | |
151 | hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk); | |
152 | new_chunk->address = BE2H_4(spec->base); | |
153 | new_chunk->size = BE2H_4(spec->size); | |
154 | new_chunk->available = 1; | |
155 | device_attach_address(device_parent(me), | |
156 | device_name(me), | |
157 | attach_raw_memory, | |
158 | 0 /*address space*/, | |
159 | new_chunk->address, | |
160 | new_chunk->size, | |
161 | access_read_write_exec, | |
162 | me); | |
163 | spec++; | |
164 | nr_entries--; | |
165 | *curr_chunk = new_chunk; | |
166 | curr_chunk = &new_chunk->next; | |
167 | } | |
168 | } | |
169 | ||
170 | /* initialize the alloc property for this device */ | |
171 | hw_memory_set_available(me, hw_memory); | |
172 | } | |
173 | ||
174 | static void | |
175 | hw_memory_instance_delete(device_instance *instance) | |
176 | { | |
177 | return; | |
178 | } | |
179 | ||
180 | static unsigned_word | |
181 | hw_memory_instance_claim(device_instance *instance, | |
182 | unsigned_word address, | |
183 | unsigned_word size, | |
184 | unsigned_word alignment) | |
185 | { | |
186 | hw_memory_device *hw_memory = device_instance_data(instance); | |
187 | hw_memory_chunk *chunk = NULL; | |
188 | DTRACE(memory, ("claim - address=0x%lx size=0x%lx alignment=%d\n", | |
189 | (unsigned long)address, | |
190 | (unsigned long)size, | |
191 | (int)alignment)); | |
192 | /* find a chunk candidate, either according to address or alignment */ | |
193 | if (alignment == 0) { | |
194 | chunk = hw_memory->heap; | |
195 | while (chunk != NULL | |
196 | && (address+size) > (chunk->address+chunk->size)) | |
197 | chunk = chunk->next; | |
198 | if (chunk == NULL || address < chunk->address || !chunk->available) | |
199 | error("hw_memory_instance_claim: failed to allocate @0x%lx, size %ld\n", | |
200 | (unsigned long)address, (unsigned long)size); | |
201 | } | |
202 | else { | |
203 | chunk = hw_memory->heap; | |
204 | while (chunk != NULL && chunk->size < size) | |
205 | chunk = chunk->next; | |
206 | if (chunk == NULL || FLOOR_PAGE(alignment-1) > 0) | |
207 | error("hw_memory_instance_claim: failed to allocate %ld, align %ld\n", | |
208 | (unsigned long)size, (unsigned long)size); | |
209 | address = chunk->address; | |
210 | } | |
211 | /* break of a part before this memory if needed */ | |
212 | ASSERT(address >= chunk->address); | |
213 | if (FLOOR_PAGE(address) > chunk->address) { | |
214 | hw_memory_chunk *last_chunk = chunk; | |
215 | /* insert a new earlier chunk */ | |
216 | chunk = ZALLOC(hw_memory_chunk); | |
217 | chunk->next = last_chunk->next; | |
218 | last_chunk->next = chunk; | |
219 | /* adjust the address/size */ | |
220 | chunk->address = FLOOR_PAGE(address); | |
221 | chunk->size = last_chunk->size - (chunk->address - last_chunk->address); | |
222 | last_chunk->size = chunk->address - last_chunk->address; | |
223 | } | |
224 | ASSERT(FLOOR_PAGE(address) == chunk->address); | |
225 | /* break of a bit after this chunk if needed */ | |
226 | if (ALIGN_PAGE(address+size) < chunk->address + chunk->size) { | |
227 | hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk); | |
228 | /* insert it in to the list */ | |
229 | next_chunk->next = chunk->next; | |
230 | chunk->next = next_chunk; | |
231 | next_chunk->available = 1; | |
232 | /* adjust the address/size */ | |
233 | next_chunk->address = ALIGN_PAGE(address+size); | |
234 | next_chunk->size = chunk->address + chunk->size - next_chunk->address; | |
235 | chunk->size = next_chunk->address - chunk->address; | |
236 | } | |
237 | ASSERT(ALIGN_PAGE(address+size) == chunk->address + chunk->size); | |
238 | /* now allocate it */ | |
239 | chunk->alloc_address = address; | |
240 | chunk->alloc_size = size; | |
241 | chunk->available = 0; | |
242 | hw_memory_set_available(device_instance_device(instance), hw_memory); | |
243 | return address; | |
244 | } | |
245 | ||
246 | static void | |
247 | hw_memory_instance_release(device_instance *instance, | |
248 | unsigned_word address, | |
249 | unsigned_word length) | |
250 | { | |
251 | hw_memory_device *hw_memory = device_instance_data(instance); | |
252 | hw_memory_chunk *chunk = hw_memory->heap; | |
253 | while (chunk != NULL) { | |
254 | if (chunk->alloc_address == address | |
255 | && chunk->alloc_size == length | |
256 | && chunk->available == 0) { | |
257 | /* free this chunk */ | |
258 | chunk->available = 1; | |
259 | /* check for merge */ | |
260 | chunk = hw_memory->heap; | |
261 | while (chunk != NULL) { | |
262 | if (chunk->available | |
263 | && chunk->next != NULL && chunk->next->available) { | |
264 | /* adjacent */ | |
265 | hw_memory_chunk *delete = chunk->next; | |
266 | ASSERT(chunk->address + chunk->size == delete->address); | |
267 | chunk->size += delete->size; | |
268 | chunk->next = delete->next; | |
269 | zfree(delete); | |
270 | } | |
271 | } | |
272 | /* update the corresponding property */ | |
273 | hw_memory_set_available(device_instance_device(instance), hw_memory); | |
274 | return; | |
275 | } | |
276 | chunk = chunk->next; | |
277 | } | |
278 | error("hw_memory_instance_release: Address 0x%lx, size %ld not found\n", | |
279 | (unsigned long)address, (unsigned long)length); | |
280 | /* FIXME - dump allocated */ | |
281 | /* FIXME - dump arguments */ | |
282 | } | |
283 | ||
284 | static device_instance_callbacks const hw_memory_instance_callbacks = { | |
285 | hw_memory_instance_delete, | |
286 | NULL /*read*/, NULL /*write*/, NULL /*seek*/, | |
287 | hw_memory_instance_claim, hw_memory_instance_release | |
288 | }; | |
289 | ||
290 | static device_instance * | |
291 | hw_memory_create_instance(device *me, | |
292 | const char *path, | |
293 | const char *args) | |
294 | { | |
295 | return device_create_instance_from(me, NULL, | |
296 | device_data(me), /* nothing better */ | |
297 | path, args, | |
298 | &hw_memory_instance_callbacks); | |
299 | } | |
300 | ||
301 | static device_callbacks const hw_memory_callbacks = { | |
302 | { hw_memory_init_address, }, | |
303 | { NULL, }, /* address */ | |
304 | { NULL, }, /* IO */ | |
305 | { NULL, }, /* DMA */ | |
306 | { NULL, }, /* interrupt */ | |
307 | { NULL, }, /* unit */ | |
308 | hw_memory_create_instance, | |
309 | }; | |
310 | ||
311 | const device_descriptor hw_memory_device_descriptor[] = { | |
312 | { "memory", hw_memory_create, &hw_memory_callbacks }, | |
313 | { NULL }, | |
314 | }; | |
315 | ||
316 | #endif /* _HW_MEMORY_C_ */ |