1 /* This file is part of the program psim.
3 Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>
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.
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.
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/>.
24 #ifndef STATIC_INLINE_HW_MEMORY
25 #define STATIC_INLINE_HW_MEMORY STATIC_INLINE
28 #include "device_table.h"
33 memory - description of system memory
39 This device describes the size and location of the banks of
40 physical memory within the simulation.
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.
50 reg = { <address> <size> } (required)
52 Each pair specify one bank of memory.
54 available = { <address> <size> } (automatic)
56 Each pair specifies a block of memory that is currently unallocated.
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
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).
70 Significant work will be required before the <<memory>> device can
71 support 64bit addresses (#address-cells equal two).
75 typedef struct _memory_reg_spec
{
80 typedef struct _hw_memory_chunk hw_memory_chunk
;
81 struct _hw_memory_chunk
{
82 unsigned_word address
;
85 hw_memory_chunk
*next
;
88 typedef struct _hw_memory_device
{
89 hw_memory_chunk
*heap
;
94 hw_memory_create(const char *name
,
95 const device_unit
*unit_address
,
98 hw_memory_device
*hw_memory
= ZALLOC(hw_memory_device
);
104 hw_memory_set_available(device
*me
,
105 hw_memory_device
*hw_memory
)
107 hw_memory_chunk
*chunk
= NULL
;
108 memory_reg_spec
*available
= NULL
;
109 int nr_available
= 0;
111 int sizeof_available
= 0;
112 /* determine the nr of available chunks */
113 chunk
= hw_memory
->heap
;
115 while (chunk
!= NULL
) {
116 if (chunk
->available
)
118 ASSERT(chunk
->next
== NULL
119 || chunk
->address
< chunk
->next
->address
);
120 ASSERT(chunk
->next
== NULL
121 || chunk
->address
+ chunk
->size
== chunk
->next
->address
);
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
;
130 while (chunk
!= NULL
) {
131 if (chunk
->available
) {
132 available
[curr
].base
= H2BE_cell(chunk
->address
);
133 available
[curr
].size
= H2BE_cell(chunk
->size
);
139 device_set_array_property(me
, "available", available
, sizeof_available
);
145 hw_memory_init_address(device
*me
)
147 hw_memory_device
*hw_memory
= (hw_memory_device
*)device_data(me
);
149 /* free up any previous structures */
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
;
161 /* attach memory regions according to the "reg" property */
164 reg_property_spec reg
;
166 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
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
),
180 reg
.address
.cells
[reg
.address
.nr_cells
- 1],
181 reg
.size
.cells
[reg
.size
.nr_cells
- 1],
182 access_read_write_exec
,
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
;
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");
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,
203 new_chunk
->available
= 1;
204 *curr_chunk
= new_chunk
;
205 curr_chunk
= &new_chunk
->next
;
209 hw_memory_chunk
**curr_chunk
= &hw_memory
->heap
;
211 reg_property_spec reg
;
213 device_find_reg_array_property(me
, "reg", 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
;
225 /* initialize the alloc property for this device */
226 hw_memory_set_available(me
, hw_memory
);
230 hw_memory_instance_delete(device_instance
*instance
)
236 hw_memory_instance_claim(device_instance
*instance
,
238 unsigned_cell stack_args
[/*n_stack_args*/],
240 unsigned_cell stack_returns
[/*n_stack_returns*/])
242 hw_memory_device
*hw_memory
= device_instance_data(instance
);
243 device
*me
= device_instance_device(instance
);
245 unsigned_word alignment
;
247 unsigned_cell address
;
248 hw_memory_chunk
*chunk
= NULL
;
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
];
256 /* get the size from the stack */
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");
267 size
= stack_args
[stackp
];
271 /* get the address from the stack */
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"));
279 device_error(me
, "claim - incorrect number of arguments (optional addr)");
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");
292 address
= stack_args
[stackp
];
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");
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
))
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
));
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)
322 device_error(me
, "alignment 0x%lx is to large", (unsigned long)alignment
);
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
))
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",
338 (unsigned long)alignment
,
339 (unsigned long)alignment
,
340 (unsigned long)address
));
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 */
358 ASSERT(address
== chunk
->address
);
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
;
373 ASSERT(address
+ size
== chunk
->address
+ chunk
->size
);
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) {
380 for (i
= 0; i
< n_stack_returns
- 1; i
++)
381 stack_returns
[i
] = 0;
382 stack_returns
[n_stack_returns
- 1] = address
;
390 hw_memory_instance_release(device_instance
*instance
,
392 unsigned_cell stack_args
[/*n_stack_args*/],
394 unsigned_cell stack_returns
[/*n_stack_returns*/])
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
;
401 hw_memory_chunk
*chunk
;
403 /* get the length from the stack */
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");
414 length
= stack_args
[stackp
];
418 /* get the address from the stack */
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");
429 address
= stack_args
[stackp
];
433 if (n_stack_returns
!= 0)
434 device_error(me
, "release - nonzero number of results");
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
) {
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
);
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;
455 else if (chunk
->address
>= address
456 && chunk
->address
+ chunk
->size
<= address
+ length
) {
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;
470 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
471 (unsigned long)address
,
472 (unsigned long)(address
+ length
- 1));
475 /* check for the chance to merge two adjacent available memory chunks */
476 chunk
= hw_memory
->heap
;
477 while (chunk
!= NULL
) {
479 && chunk
->next
!= NULL
&& chunk
->next
->available
) {
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
;
492 /* update the corresponding property */
493 hw_memory_set_available(device_instance_device(instance
), hw_memory
);
499 static device_instance_methods hw_memory_instance_methods
[] = {
500 { "claim", hw_memory_instance_claim
},
501 { "release", hw_memory_instance_release
},
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
511 static device_instance
*
512 hw_memory_create_instance(device
*me
,
516 return device_create_instance_from(me
, NULL
,
517 device_data(me
), /* nothing better */
519 &hw_memory_instance_callbacks
);
522 static device_callbacks
const hw_memory_callbacks
= {
523 { hw_memory_init_address
, },
524 { NULL
, }, /* address */
527 { NULL
, }, /* interrupt */
528 { NULL
, }, /* unit */
529 hw_memory_create_instance
,
532 const device_descriptor hw_memory_device_descriptor
[] = {
533 { "memory", hw_memory_create
, &hw_memory_callbacks
},
537 #endif /* _HW_MEMORY_C_ */