[sim] Update old contact info in GPL license notices
[deliverable/binutils-gdb.git] / sim / ppc / hw_memory.c
1 /* This file is part of the program psim.
2
3 Copyright (C) 1994-1997, 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 3 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, see <http://www.gnu.org/licenses/>.
17
18 */
19
20
21 #ifndef _HW_MEMORY_C_
22 #define _HW_MEMORY_C_
23
24 #ifndef STATIC_INLINE_HW_MEMORY
25 #define STATIC_INLINE_HW_MEMORY STATIC_INLINE
26 #endif
27
28 #include "device_table.h"
29
30 /* DEVICE
31
32
33 memory - description of system memory
34
35
36 DESCRIPTION
37
38
39 This device describes the size and location of the banks of
40 physical memory within the simulation.
41
42 In addition, this device supports the "claim" and "release" methods
43 that can be used by OpenBoot client programs to manage the
44 allocation of physical memory.
45
46
47 PROPERTIES
48
49
50 reg = { <address> <size> } (required)
51
52 Each pair specify one bank of memory.
53
54 available = { <address> <size> } (automatic)
55
56 Each pair specifies a block of memory that is currently unallocated.
57
58
59 BUGS
60
61
62 OpenFirmware doesn't make it clear if, when releasing memory the
63 same address + size pair as was used during the claim should be
64 specified.
65
66 It is assumed that #size-cells and #address-cells for the parent
67 node of this device are both one i.e. an address or size can be
68 specified using a single memory cell (word).
69
70 Significant work will be required before the <<memory>> device can
71 support 64bit addresses (#address-cells equal two).
72
73 */
74
75 typedef struct _memory_reg_spec {
76 unsigned_cell base;
77 unsigned_cell size;
78 } memory_reg_spec;
79
80 typedef struct _hw_memory_chunk hw_memory_chunk;
81 struct _hw_memory_chunk {
82 unsigned_word address;
83 unsigned_word size;
84 int available;
85 hw_memory_chunk *next;
86 };
87
88 typedef struct _hw_memory_device {
89 hw_memory_chunk *heap;
90 } hw_memory_device;
91
92
93 static void *
94 hw_memory_create(const char *name,
95 const device_unit *unit_address,
96 const char *args)
97 {
98 hw_memory_device *hw_memory = ZALLOC(hw_memory_device);
99 return hw_memory;
100 }
101
102
103 static void
104 hw_memory_set_available(device *me,
105 hw_memory_device *hw_memory)
106 {
107 hw_memory_chunk *chunk = NULL;
108 memory_reg_spec *available = NULL;
109 int nr_available = 0;
110 int curr = 0;
111 int sizeof_available = 0;
112 /* determine the nr of available chunks */
113 chunk = hw_memory->heap;
114 nr_available = 0;
115 while (chunk != NULL) {
116 if (chunk->available)
117 nr_available += 1;
118 ASSERT(chunk->next == NULL
119 || chunk->address < chunk->next->address);
120 ASSERT(chunk->next == NULL
121 || chunk->address + chunk->size == chunk->next->address);
122 chunk = chunk->next;
123 }
124 /* now create the available struct */
125 ASSERT(nr_available > 0);
126 sizeof_available = sizeof(memory_reg_spec) * nr_available;
127 available = zalloc(sizeof_available);
128 chunk = hw_memory->heap;
129 curr = 0;
130 while (chunk != NULL) {
131 if (chunk->available) {
132 available[curr].base = H2BE_cell(chunk->address);
133 available[curr].size = H2BE_cell(chunk->size);
134 curr += 1;
135 }
136 chunk = chunk->next;
137 }
138 /* update */
139 device_set_array_property(me, "available", available, sizeof_available);
140 free(available);
141 }
142
143
144 static void
145 hw_memory_init_address(device *me)
146 {
147 hw_memory_device *hw_memory = (hw_memory_device*)device_data(me);
148
149 /* free up any previous structures */
150 {
151 hw_memory_chunk *curr_chunk = hw_memory->heap;
152 hw_memory->heap = NULL;
153 while (curr_chunk != NULL) {
154 hw_memory_chunk *dead_chunk = curr_chunk;
155 curr_chunk = dead_chunk->next;
156 dead_chunk->next = NULL;
157 free(dead_chunk);
158 }
159 }
160
161 /* attach memory regions according to the "reg" property */
162 {
163 int reg_nr;
164 reg_property_spec reg;
165 for (reg_nr = 0;
166 device_find_reg_array_property(me, "reg", reg_nr, &reg);
167 reg_nr++) {
168 int i;
169 /* check that the entry meets restrictions */
170 for (i = 0; i < reg.address.nr_cells - 1; i++)
171 if (reg.address.cells[i] != 0)
172 device_error(me, "Only single celled addresses supported");
173 for (i = 0; i < reg.size.nr_cells - 1; i++)
174 if (reg.size.cells[i] != 0)
175 device_error(me, "Only single celled sizes supported");
176 /* attach the range */
177 device_attach_address(device_parent(me),
178 attach_raw_memory,
179 0 /*address space*/,
180 reg.address.cells[reg.address.nr_cells - 1],
181 reg.size.cells[reg.size.nr_cells - 1],
182 access_read_write_exec,
183 me);
184 }
185 }
186
187 /* create the initial `available memory' data structure */
188 if (device_find_property(me, "available") != NULL) {
189 hw_memory_chunk **curr_chunk = &hw_memory->heap;
190 int cell_nr;
191 unsigned_cell dummy;
192 int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy);
193 if ((nr_cells % 2) != 0)
194 device_error(me, "property \"available\" invalid - contains an odd number of cells");
195 for (cell_nr = 0;
196 cell_nr < nr_cells;
197 cell_nr += 2) {
198 hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk);
199 device_find_integer_array_property(me, "available", cell_nr,
200 &new_chunk->address);
201 device_find_integer_array_property(me, "available", cell_nr + 1,
202 &new_chunk->size);
203 new_chunk->available = 1;
204 *curr_chunk = new_chunk;
205 curr_chunk = &new_chunk->next;
206 }
207 }
208 else {
209 hw_memory_chunk **curr_chunk = &hw_memory->heap;
210 int reg_nr;
211 reg_property_spec reg;
212 for (reg_nr = 0;
213 device_find_reg_array_property(me, "reg", reg_nr, &reg);
214 reg_nr++) {
215 hw_memory_chunk *new_chunk;
216 new_chunk = ZALLOC(hw_memory_chunk);
217 new_chunk->address = reg.address.cells[reg.address.nr_cells - 1];
218 new_chunk->size = reg.size.cells[reg.size.nr_cells - 1];
219 new_chunk->available = 1;
220 *curr_chunk = new_chunk;
221 curr_chunk = &new_chunk->next;
222 }
223 }
224
225 /* initialize the alloc property for this device */
226 hw_memory_set_available(me, hw_memory);
227 }
228
229 static void
230 hw_memory_instance_delete(device_instance *instance)
231 {
232 return;
233 }
234
235 static int
236 hw_memory_instance_claim(device_instance *instance,
237 int n_stack_args,
238 unsigned_cell stack_args[/*n_stack_args*/],
239 int n_stack_returns,
240 unsigned_cell stack_returns[/*n_stack_returns*/])
241 {
242 hw_memory_device *hw_memory = device_instance_data(instance);
243 device *me = device_instance_device(instance);
244 int stackp = 0;
245 unsigned_word alignment;
246 unsigned_cell size;
247 unsigned_cell address;
248 hw_memory_chunk *chunk = NULL;
249
250 /* get the alignment from the stack */
251 if (n_stack_args < stackp + 1)
252 device_error(me, "claim - incorrect number of arguments (alignment missing)");
253 alignment = stack_args[stackp];
254 stackp++;
255
256 /* get the size from the stack */
257 {
258 int i;
259 int nr_cells = device_nr_size_cells(device_parent(me));
260 if (n_stack_args < stackp + nr_cells)
261 device_error(me, "claim - incorrect number of arguments (size missing)");
262 for (i = 0; i < nr_cells - 1; i++) {
263 if (stack_args[stackp] != 0)
264 device_error(me, "claim - multi-cell sizes not supported");
265 stackp++;
266 }
267 size = stack_args[stackp];
268 stackp++;
269 }
270
271 /* get the address from the stack */
272 {
273 int nr_cells = device_nr_address_cells(device_parent(me));
274 if (alignment != 0) {
275 if (n_stack_args != stackp) {
276 if (n_stack_args == stackp + nr_cells)
277 DTRACE(memory, ("claim - extra address argument ignored\n"));
278 else
279 device_error(me, "claim - incorrect number of arguments (optional addr)");
280 }
281 address = 0;
282 }
283 else {
284 int i;
285 if (n_stack_args != stackp + nr_cells)
286 device_error(me, "claim - incorrect number of arguments (addr missing)");
287 for (i = 0; i < nr_cells - 1; i++) {
288 if (stack_args[stackp] != 0)
289 device_error(me, "claim - multi-cell addresses not supported");
290 stackp++;
291 }
292 address = stack_args[stackp];
293 }
294 }
295
296 /* check that there is space for the result */
297 if (n_stack_returns != 0
298 && n_stack_returns != device_nr_address_cells(device_parent(me)))
299 device_error(me, "claim - invalid number of return arguments");
300
301 /* find a chunk candidate, either according to address or alignment */
302 if (alignment == 0) {
303 chunk = hw_memory->heap;
304 while (chunk != NULL) {
305 if ((address + size) <= (chunk->address + chunk->size))
306 break;
307 chunk = chunk->next;
308 }
309 if (chunk == NULL || address < chunk->address || !chunk->available)
310 device_error(me, "failed to allocate %ld bytes at 0x%lx",
311 (unsigned long)size, (unsigned long)address);
312 DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n",
313 (unsigned long)address,
314 (unsigned long)size));
315 }
316 else {
317 /* adjust the alignment so that it is a power of two */
318 unsigned_word align_mask = 1;
319 while (align_mask < alignment && align_mask != 0)
320 align_mask <<= 1;
321 if (align_mask == 0)
322 device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment);
323 align_mask -= 1;
324 /* now find an aligned chunk that fits */
325 chunk = hw_memory->heap;
326 while (chunk != NULL) {
327 address = ((chunk->address + align_mask) & ~align_mask);
328 if ((chunk->available)
329 && (chunk->address + chunk->size >= address + size))
330 break;
331 chunk = chunk->next;
332 }
333 if (chunk == NULL)
334 device_error(me, "failed to allocate %ld bytes with alignment %ld",
335 (unsigned long)size, (unsigned long)alignment);
336 DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
337 (unsigned long)size,
338 (unsigned long)alignment,
339 (unsigned long)alignment,
340 (unsigned long)address));
341 }
342
343 /* break off a bit before this chunk if needed */
344 ASSERT(address >= chunk->address);
345 if (address > chunk->address) {
346 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
347 /* insert a new chunk */
348 next_chunk->next = chunk->next;
349 chunk->next = next_chunk;
350 /* adjust the address/size */
351 next_chunk->address = address;
352 next_chunk->size = chunk->address + chunk->size - next_chunk->address;
353 next_chunk->available = 1;
354 chunk->size = next_chunk->address - chunk->address;
355 /* make this new chunk the one to allocate */
356 chunk = next_chunk;
357 }
358 ASSERT(address == chunk->address);
359
360 /* break off a bit after this chunk if needed */
361 ASSERT(address + size <= chunk->address + chunk->size);
362 if (address + size < chunk->address + chunk->size) {
363 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
364 /* insert it in to the list */
365 next_chunk->next = chunk->next;
366 chunk->next = next_chunk;
367 /* adjust the address/size */
368 next_chunk->address = address + size;
369 next_chunk->size = chunk->address + chunk->size - next_chunk->address;
370 next_chunk->available = 1;
371 chunk->size = next_chunk->address - chunk->address;
372 }
373 ASSERT(address + size == chunk->address + chunk->size);
374
375 /* now allocate/return it */
376 chunk->available = 0;
377 hw_memory_set_available(device_instance_device(instance), hw_memory);
378 if (n_stack_returns > 0) {
379 int i;
380 for (i = 0; i < n_stack_returns - 1; i++)
381 stack_returns[i] = 0;
382 stack_returns[n_stack_returns - 1] = address;
383 }
384
385 return 0;
386 }
387
388
389 static int
390 hw_memory_instance_release(device_instance *instance,
391 int n_stack_args,
392 unsigned_cell stack_args[/*n_stack_args*/],
393 int n_stack_returns,
394 unsigned_cell stack_returns[/*n_stack_returns*/])
395 {
396 hw_memory_device *hw_memory = device_instance_data(instance);
397 device *me = device_instance_device(instance);
398 unsigned_word length;
399 unsigned_word address;
400 int stackp = 0;
401 hw_memory_chunk *chunk;
402
403 /* get the length from the stack */
404 {
405 int i;
406 int nr_cells = device_nr_size_cells(device_parent(me));
407 if (n_stack_args < stackp + nr_cells)
408 device_error(me, "release - incorrect number of arguments (length missing)");
409 for (i = 0; i < nr_cells - 1; i++) {
410 if (stack_args[stackp] != 0)
411 device_error(me, "release - multi-cell length not supported");
412 stackp++;
413 }
414 length = stack_args[stackp];
415 stackp++;
416 }
417
418 /* get the address from the stack */
419 {
420 int i;
421 int nr_cells = device_nr_address_cells(device_parent(me));
422 if (n_stack_args != stackp + nr_cells)
423 device_error(me, "release - incorrect number of arguments (addr missing)");
424 for (i = 0; i < nr_cells - 1; i++) {
425 if (stack_args[stackp] != 0)
426 device_error(me, "release - multi-cell addresses not supported");
427 stackp++;
428 }
429 address = stack_args[stackp];
430 }
431
432 /* returns ok */
433 if (n_stack_returns != 0)
434 device_error(me, "release - nonzero number of results");
435
436 /* try to free the corresponding memory chunk */
437 chunk = hw_memory->heap;
438 while (chunk != NULL) {
439 if (chunk->address == address
440 && chunk->size == length) {
441 /* an exact match */
442 if (chunk->available)
443 device_error(me, "memory chunk 0x%lx (size 0x%lx) already available",
444 (unsigned long)address,
445 (unsigned long)length);
446 else {
447 /* free this chunk */
448 DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n",
449 (unsigned long) address,
450 (unsigned long) length));
451 chunk->available = 1;
452 break;
453 }
454 }
455 else if (chunk->address >= address
456 && chunk->address + chunk->size <= address + length) {
457 /* a sub region */
458 if (!chunk->available) {
459 DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
460 (unsigned long) chunk->address,
461 (unsigned long) chunk->size,
462 (unsigned long) address,
463 (unsigned long) length));
464 chunk->available = 1;
465 }
466 }
467 chunk = chunk->next;
468 }
469 if (chunk == NULL) {
470 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
471 (unsigned long)address,
472 (unsigned long)(address + length - 1));
473 }
474
475 /* check for the chance to merge two adjacent available memory chunks */
476 chunk = hw_memory->heap;
477 while (chunk != NULL) {
478 if (chunk->available
479 && chunk->next != NULL && chunk->next->available) {
480 /* adjacent */
481 hw_memory_chunk *delete = chunk->next;
482 ASSERT(chunk->address + chunk->size == delete->address);
483 chunk->size += delete->size;
484 chunk->next = delete->next;
485 free(delete);
486 }
487 else {
488 chunk = chunk->next;
489 }
490 }
491
492 /* update the corresponding property */
493 hw_memory_set_available(device_instance_device(instance), hw_memory);
494
495 return 0;
496 }
497
498
499 static device_instance_methods hw_memory_instance_methods[] = {
500 { "claim", hw_memory_instance_claim },
501 { "release", hw_memory_instance_release },
502 { NULL, },
503 };
504
505 static device_instance_callbacks const hw_memory_instance_callbacks = {
506 hw_memory_instance_delete,
507 NULL /*read*/, NULL /*write*/, NULL /*seek*/,
508 hw_memory_instance_methods
509 };
510
511 static device_instance *
512 hw_memory_create_instance(device *me,
513 const char *path,
514 const char *args)
515 {
516 return device_create_instance_from(me, NULL,
517 device_data(me), /* nothing better */
518 path, args,
519 &hw_memory_instance_callbacks);
520 }
521
522 static device_callbacks const hw_memory_callbacks = {
523 { hw_memory_init_address, },
524 { NULL, }, /* address */
525 { NULL, }, /* IO */
526 { NULL, }, /* DMA */
527 { NULL, }, /* interrupt */
528 { NULL, }, /* unit */
529 hw_memory_create_instance,
530 };
531
532 const device_descriptor hw_memory_device_descriptor[] = {
533 { "memory", hw_memory_create, &hw_memory_callbacks },
534 { NULL },
535 };
536
537 #endif /* _HW_MEMORY_C_ */
This page took 0.062437 seconds and 4 git commands to generate.