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
30 #include "device_table.h"
35 memory - description of system memory
41 This device describes the size and location of the banks of
42 physical memory within the simulation.
44 In addition, this device supports the "claim" and "release" methods
45 that can be used by OpenBoot client programs to manage the
46 allocation of physical memory.
52 reg = { <address> <size> } (required)
54 Each pair specify one bank of memory.
56 available = { <address> <size> } (automatic)
58 Each pair specifies a block of memory that is currently unallocated.
64 OpenFirmware doesn't make it clear if, when releasing memory the
65 same address + size pair as was used during the claim should be
68 It is assumed that #size-cells and #address-cells for the parent
69 node of this device are both one i.e. an address or size can be
70 specified using a single memory cell (word).
72 Significant work will be required before the <<memory>> device can
73 support 64bit addresses (#address-cells equal two).
77 typedef struct _memory_reg_spec
{
82 typedef struct _hw_memory_chunk hw_memory_chunk
;
83 struct _hw_memory_chunk
{
84 unsigned_word address
;
87 hw_memory_chunk
*next
;
90 typedef struct _hw_memory_device
{
91 hw_memory_chunk
*heap
;
96 hw_memory_create(const char *name
,
97 const device_unit
*unit_address
,
100 hw_memory_device
*hw_memory
= ZALLOC(hw_memory_device
);
106 hw_memory_set_available(device
*me
,
107 hw_memory_device
*hw_memory
)
109 hw_memory_chunk
*chunk
= NULL
;
110 memory_reg_spec
*available
= NULL
;
111 int nr_available
= 0;
113 int sizeof_available
= 0;
114 /* determine the nr of available chunks */
115 chunk
= hw_memory
->heap
;
117 while (chunk
!= NULL
) {
118 if (chunk
->available
)
120 ASSERT(chunk
->next
== NULL
121 || chunk
->address
< chunk
->next
->address
);
122 ASSERT(chunk
->next
== NULL
123 || chunk
->address
+ chunk
->size
== chunk
->next
->address
);
126 /* now create the available struct */
127 ASSERT(nr_available
> 0);
128 sizeof_available
= sizeof(memory_reg_spec
) * nr_available
;
129 available
= zalloc(sizeof_available
);
130 chunk
= hw_memory
->heap
;
132 while (chunk
!= NULL
) {
133 if (chunk
->available
) {
134 available
[curr
].base
= H2BE_cell(chunk
->address
);
135 available
[curr
].size
= H2BE_cell(chunk
->size
);
141 device_set_array_property(me
, "available", available
, sizeof_available
);
147 hw_memory_init_address(device
*me
)
149 hw_memory_device
*hw_memory
= (hw_memory_device
*)device_data(me
);
151 /* free up any previous structures */
153 hw_memory_chunk
*curr_chunk
= hw_memory
->heap
;
154 hw_memory
->heap
= NULL
;
155 while (curr_chunk
!= NULL
) {
156 hw_memory_chunk
*dead_chunk
= curr_chunk
;
157 curr_chunk
= dead_chunk
->next
;
158 dead_chunk
->next
= NULL
;
163 /* attach memory regions according to the "reg" property */
166 reg_property_spec reg
;
168 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
171 /* check that the entry meets restrictions */
172 for (i
= 0; i
< reg
.address
.nr_cells
- 1; i
++)
173 if (reg
.address
.cells
[i
] != 0)
174 device_error(me
, "Only single celled addresses supported");
175 for (i
= 0; i
< reg
.size
.nr_cells
- 1; i
++)
176 if (reg
.size
.cells
[i
] != 0)
177 device_error(me
, "Only single celled sizes supported");
178 /* attach the range */
179 device_attach_address(device_parent(me
),
182 reg
.address
.cells
[reg
.address
.nr_cells
- 1],
183 reg
.size
.cells
[reg
.size
.nr_cells
- 1],
184 access_read_write_exec
,
189 /* create the initial `available memory' data structure */
190 if (device_find_property(me
, "available") != NULL
) {
191 hw_memory_chunk
**curr_chunk
= &hw_memory
->heap
;
194 int nr_cells
= device_find_integer_array_property(me
, "available", 0, &dummy
);
195 if ((nr_cells
% 2) != 0)
196 device_error(me
, "property \"available\" invalid - contains an odd number of cells");
200 hw_memory_chunk
*new_chunk
= ZALLOC(hw_memory_chunk
);
201 device_find_integer_array_property(me
, "available", cell_nr
,
202 &new_chunk
->address
);
203 device_find_integer_array_property(me
, "available", cell_nr
+ 1,
205 new_chunk
->available
= 1;
206 *curr_chunk
= new_chunk
;
207 curr_chunk
= &new_chunk
->next
;
211 hw_memory_chunk
**curr_chunk
= &hw_memory
->heap
;
213 reg_property_spec reg
;
215 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
217 hw_memory_chunk
*new_chunk
;
218 new_chunk
= ZALLOC(hw_memory_chunk
);
219 new_chunk
->address
= reg
.address
.cells
[reg
.address
.nr_cells
- 1];
220 new_chunk
->size
= reg
.size
.cells
[reg
.size
.nr_cells
- 1];
221 new_chunk
->available
= 1;
222 *curr_chunk
= new_chunk
;
223 curr_chunk
= &new_chunk
->next
;
227 /* initialize the alloc property for this device */
228 hw_memory_set_available(me
, hw_memory
);
232 hw_memory_instance_delete(device_instance
*instance
)
238 hw_memory_instance_claim(device_instance
*instance
,
240 unsigned_cell stack_args
[/*n_stack_args*/],
242 unsigned_cell stack_returns
[/*n_stack_returns*/])
244 hw_memory_device
*hw_memory
= device_instance_data(instance
);
245 device
*me
= device_instance_device(instance
);
247 unsigned_word alignment
;
249 unsigned_cell address
;
250 hw_memory_chunk
*chunk
= NULL
;
252 /* get the alignment from the stack */
253 if (n_stack_args
< stackp
+ 1)
254 device_error(me
, "claim - incorrect number of arguments (alignment missing)");
255 alignment
= stack_args
[stackp
];
258 /* get the size from the stack */
261 int nr_cells
= device_nr_size_cells(device_parent(me
));
262 if (n_stack_args
< stackp
+ nr_cells
)
263 device_error(me
, "claim - incorrect number of arguments (size missing)");
264 for (i
= 0; i
< nr_cells
- 1; i
++) {
265 if (stack_args
[stackp
] != 0)
266 device_error(me
, "claim - multi-cell sizes not supported");
269 size
= stack_args
[stackp
];
273 /* get the address from the stack */
275 int nr_cells
= device_nr_address_cells(device_parent(me
));
276 if (alignment
!= 0) {
277 if (n_stack_args
!= stackp
) {
278 if (n_stack_args
== stackp
+ nr_cells
)
279 DTRACE(memory
, ("claim - extra address argument ignored\n"));
281 device_error(me
, "claim - incorrect number of arguments (optional addr)");
287 if (n_stack_args
!= stackp
+ nr_cells
)
288 device_error(me
, "claim - incorrect number of arguments (addr missing)");
289 for (i
= 0; i
< nr_cells
- 1; i
++) {
290 if (stack_args
[stackp
] != 0)
291 device_error(me
, "claim - multi-cell addresses not supported");
294 address
= stack_args
[stackp
];
298 /* check that there is space for the result */
299 if (n_stack_returns
!= 0
300 && n_stack_returns
!= device_nr_address_cells(device_parent(me
)))
301 device_error(me
, "claim - invalid number of return arguments");
303 /* find a chunk candidate, either according to address or alignment */
304 if (alignment
== 0) {
305 chunk
= hw_memory
->heap
;
306 while (chunk
!= NULL
) {
307 if ((address
+ size
) <= (chunk
->address
+ chunk
->size
))
311 if (chunk
== NULL
|| address
< chunk
->address
|| !chunk
->available
)
312 device_error(me
, "failed to allocate %ld bytes at 0x%lx",
313 (unsigned long)size
, (unsigned long)address
);
314 DTRACE(memory
, ("claim - address=0x%lx size=0x%lx\n",
315 (unsigned long)address
,
316 (unsigned long)size
));
319 /* adjust the alignment so that it is a power of two */
320 unsigned_word align_mask
= 1;
321 while (align_mask
< alignment
&& align_mask
!= 0)
324 device_error(me
, "alignment 0x%lx is to large", (unsigned long)alignment
);
326 /* now find an aligned chunk that fits */
327 chunk
= hw_memory
->heap
;
328 while (chunk
!= NULL
) {
329 address
= ((chunk
->address
+ align_mask
) & ~align_mask
);
330 if ((chunk
->available
)
331 && (chunk
->address
+ chunk
->size
>= address
+ size
))
336 device_error(me
, "failed to allocate %ld bytes with alignment %ld",
337 (unsigned long)size
, (unsigned long)alignment
);
338 DTRACE(memory
, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
340 (unsigned long)alignment
,
341 (unsigned long)alignment
,
342 (unsigned long)address
));
345 /* break off a bit before this chunk if needed */
346 ASSERT(address
>= chunk
->address
);
347 if (address
> chunk
->address
) {
348 hw_memory_chunk
*next_chunk
= ZALLOC(hw_memory_chunk
);
349 /* insert a new chunk */
350 next_chunk
->next
= chunk
->next
;
351 chunk
->next
= next_chunk
;
352 /* adjust the address/size */
353 next_chunk
->address
= address
;
354 next_chunk
->size
= chunk
->address
+ chunk
->size
- next_chunk
->address
;
355 next_chunk
->available
= 1;
356 chunk
->size
= next_chunk
->address
- chunk
->address
;
357 /* make this new chunk the one to allocate */
360 ASSERT(address
== chunk
->address
);
362 /* break off a bit after this chunk if needed */
363 ASSERT(address
+ size
<= chunk
->address
+ chunk
->size
);
364 if (address
+ size
< chunk
->address
+ chunk
->size
) {
365 hw_memory_chunk
*next_chunk
= ZALLOC(hw_memory_chunk
);
366 /* insert it in to the list */
367 next_chunk
->next
= chunk
->next
;
368 chunk
->next
= next_chunk
;
369 /* adjust the address/size */
370 next_chunk
->address
= address
+ size
;
371 next_chunk
->size
= chunk
->address
+ chunk
->size
- next_chunk
->address
;
372 next_chunk
->available
= 1;
373 chunk
->size
= next_chunk
->address
- chunk
->address
;
375 ASSERT(address
+ size
== chunk
->address
+ chunk
->size
);
377 /* now allocate/return it */
378 chunk
->available
= 0;
379 hw_memory_set_available(device_instance_device(instance
), hw_memory
);
380 if (n_stack_returns
> 0) {
382 for (i
= 0; i
< n_stack_returns
- 1; i
++)
383 stack_returns
[i
] = 0;
384 stack_returns
[n_stack_returns
- 1] = address
;
392 hw_memory_instance_release(device_instance
*instance
,
394 unsigned_cell stack_args
[/*n_stack_args*/],
396 unsigned_cell stack_returns
[/*n_stack_returns*/])
398 hw_memory_device
*hw_memory
= device_instance_data(instance
);
399 device
*me
= device_instance_device(instance
);
400 unsigned_word length
;
401 unsigned_word address
;
403 hw_memory_chunk
*chunk
;
405 /* get the length from the stack */
408 int nr_cells
= device_nr_size_cells(device_parent(me
));
409 if (n_stack_args
< stackp
+ nr_cells
)
410 device_error(me
, "release - incorrect number of arguments (length missing)");
411 for (i
= 0; i
< nr_cells
- 1; i
++) {
412 if (stack_args
[stackp
] != 0)
413 device_error(me
, "release - multi-cell length not supported");
416 length
= stack_args
[stackp
];
420 /* get the address from the stack */
423 int nr_cells
= device_nr_address_cells(device_parent(me
));
424 if (n_stack_args
!= stackp
+ nr_cells
)
425 device_error(me
, "release - incorrect number of arguments (addr missing)");
426 for (i
= 0; i
< nr_cells
- 1; i
++) {
427 if (stack_args
[stackp
] != 0)
428 device_error(me
, "release - multi-cell addresses not supported");
431 address
= stack_args
[stackp
];
435 if (n_stack_returns
!= 0)
436 device_error(me
, "release - nonzero number of results");
438 /* try to free the corresponding memory chunk */
439 chunk
= hw_memory
->heap
;
440 while (chunk
!= NULL
) {
441 if (chunk
->address
== address
442 && chunk
->size
== length
) {
444 if (chunk
->available
)
445 device_error(me
, "memory chunk 0x%lx (size 0x%lx) already available",
446 (unsigned long)address
,
447 (unsigned long)length
);
449 /* free this chunk */
450 DTRACE(memory
, ("release - address=0x%lx, length=0x%lx\n",
451 (unsigned long) address
,
452 (unsigned long) length
));
453 chunk
->available
= 1;
457 else if (chunk
->address
>= address
458 && chunk
->address
+ chunk
->size
<= address
+ length
) {
460 if (!chunk
->available
) {
461 DTRACE(memory
, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
462 (unsigned long) chunk
->address
,
463 (unsigned long) chunk
->size
,
464 (unsigned long) address
,
465 (unsigned long) length
));
466 chunk
->available
= 1;
472 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
473 (unsigned long)address
,
474 (unsigned long)(address
+ length
- 1));
477 /* check for the chance to merge two adjacent available memory chunks */
478 chunk
= hw_memory
->heap
;
479 while (chunk
!= NULL
) {
481 && chunk
->next
!= NULL
&& chunk
->next
->available
) {
483 hw_memory_chunk
*delete = chunk
->next
;
484 ASSERT(chunk
->address
+ chunk
->size
== delete->address
);
485 chunk
->size
+= delete->size
;
486 chunk
->next
= delete->next
;
494 /* update the corresponding property */
495 hw_memory_set_available(device_instance_device(instance
), hw_memory
);
501 static device_instance_methods hw_memory_instance_methods
[] = {
502 { "claim", hw_memory_instance_claim
},
503 { "release", hw_memory_instance_release
},
507 static device_instance_callbacks
const hw_memory_instance_callbacks
= {
508 hw_memory_instance_delete
,
509 NULL
/*read*/, NULL
/*write*/, NULL
/*seek*/,
510 hw_memory_instance_methods
513 static device_instance
*
514 hw_memory_create_instance(device
*me
,
518 return device_create_instance_from(me
, NULL
,
519 device_data(me
), /* nothing better */
521 &hw_memory_instance_callbacks
);
524 static device_callbacks
const hw_memory_callbacks
= {
525 { hw_memory_init_address
, },
526 { NULL
, }, /* address */
529 { NULL
, }, /* interrupt */
530 { NULL
, }, /* unit */
531 hw_memory_create_instance
,
534 const device_descriptor hw_memory_device_descriptor
[] = {
535 { "memory", hw_memory_create
, &hw_memory_callbacks
},
539 #endif /* _HW_MEMORY_C_ */