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 /* This must come before any other includes. */
33 #include "device_table.h"
38 memory - description of system memory
44 This device describes the size and location of the banks of
45 physical memory within the simulation.
47 In addition, this device supports the "claim" and "release" methods
48 that can be used by OpenBoot client programs to manage the
49 allocation of physical memory.
55 reg = { <address> <size> } (required)
57 Each pair specify one bank of memory.
59 available = { <address> <size> } (automatic)
61 Each pair specifies a block of memory that is currently unallocated.
67 OpenFirmware doesn't make it clear if, when releasing memory the
68 same address + size pair as was used during the claim should be
71 It is assumed that #size-cells and #address-cells for the parent
72 node of this device are both one i.e. an address or size can be
73 specified using a single memory cell (word).
75 Significant work will be required before the <<memory>> device can
76 support 64bit addresses (#address-cells equal two).
80 typedef struct _memory_reg_spec
{
85 typedef struct _hw_memory_chunk hw_memory_chunk
;
86 struct _hw_memory_chunk
{
87 unsigned_word address
;
90 hw_memory_chunk
*next
;
93 typedef struct _hw_memory_device
{
94 hw_memory_chunk
*heap
;
99 hw_memory_create(const char *name
,
100 const device_unit
*unit_address
,
103 hw_memory_device
*hw_memory
= ZALLOC(hw_memory_device
);
109 hw_memory_set_available(device
*me
,
110 hw_memory_device
*hw_memory
)
112 hw_memory_chunk
*chunk
= NULL
;
113 memory_reg_spec
*available
= NULL
;
114 int nr_available
= 0;
116 int sizeof_available
= 0;
117 /* determine the nr of available chunks */
118 chunk
= hw_memory
->heap
;
120 while (chunk
!= NULL
) {
121 if (chunk
->available
)
123 ASSERT(chunk
->next
== NULL
124 || chunk
->address
< chunk
->next
->address
);
125 ASSERT(chunk
->next
== NULL
126 || chunk
->address
+ chunk
->size
== chunk
->next
->address
);
129 /* now create the available struct */
130 ASSERT(nr_available
> 0);
131 sizeof_available
= sizeof(memory_reg_spec
) * nr_available
;
132 available
= zalloc(sizeof_available
);
133 chunk
= hw_memory
->heap
;
135 while (chunk
!= NULL
) {
136 if (chunk
->available
) {
137 available
[curr
].base
= H2BE_cell(chunk
->address
);
138 available
[curr
].size
= H2BE_cell(chunk
->size
);
144 device_set_array_property(me
, "available", available
, sizeof_available
);
150 hw_memory_init_address(device
*me
)
152 hw_memory_device
*hw_memory
= (hw_memory_device
*)device_data(me
);
154 /* free up any previous structures */
156 hw_memory_chunk
*curr_chunk
= hw_memory
->heap
;
157 hw_memory
->heap
= NULL
;
158 while (curr_chunk
!= NULL
) {
159 hw_memory_chunk
*dead_chunk
= curr_chunk
;
160 curr_chunk
= dead_chunk
->next
;
161 dead_chunk
->next
= NULL
;
166 /* attach memory regions according to the "reg" property */
169 reg_property_spec reg
;
171 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
174 /* check that the entry meets restrictions */
175 for (i
= 0; i
< reg
.address
.nr_cells
- 1; i
++)
176 if (reg
.address
.cells
[i
] != 0)
177 device_error(me
, "Only single celled addresses supported");
178 for (i
= 0; i
< reg
.size
.nr_cells
- 1; i
++)
179 if (reg
.size
.cells
[i
] != 0)
180 device_error(me
, "Only single celled sizes supported");
181 /* attach the range */
182 device_attach_address(device_parent(me
),
185 reg
.address
.cells
[reg
.address
.nr_cells
- 1],
186 reg
.size
.cells
[reg
.size
.nr_cells
- 1],
187 access_read_write_exec
,
192 /* create the initial `available memory' data structure */
193 if (device_find_property(me
, "available") != NULL
) {
194 hw_memory_chunk
**curr_chunk
= &hw_memory
->heap
;
197 int nr_cells
= device_find_integer_array_property(me
, "available", 0, &dummy
);
198 if ((nr_cells
% 2) != 0)
199 device_error(me
, "property \"available\" invalid - contains an odd number of cells");
203 hw_memory_chunk
*new_chunk
= ZALLOC(hw_memory_chunk
);
204 device_find_integer_array_property(me
, "available", cell_nr
,
205 (signed_cell
*)&new_chunk
->address
);
206 device_find_integer_array_property(me
, "available", cell_nr
+ 1,
207 (signed_cell
*)&new_chunk
->size
);
208 new_chunk
->available
= 1;
209 *curr_chunk
= new_chunk
;
210 curr_chunk
= &new_chunk
->next
;
214 hw_memory_chunk
**curr_chunk
= &hw_memory
->heap
;
216 reg_property_spec reg
;
218 device_find_reg_array_property(me
, "reg", reg_nr
, ®
);
220 hw_memory_chunk
*new_chunk
;
221 new_chunk
= ZALLOC(hw_memory_chunk
);
222 new_chunk
->address
= reg
.address
.cells
[reg
.address
.nr_cells
- 1];
223 new_chunk
->size
= reg
.size
.cells
[reg
.size
.nr_cells
- 1];
224 new_chunk
->available
= 1;
225 *curr_chunk
= new_chunk
;
226 curr_chunk
= &new_chunk
->next
;
230 /* initialize the alloc property for this device */
231 hw_memory_set_available(me
, hw_memory
);
235 hw_memory_instance_delete(device_instance
*instance
)
241 hw_memory_instance_claim(device_instance
*instance
,
243 unsigned_cell stack_args
[/*n_stack_args*/],
245 unsigned_cell stack_returns
[/*n_stack_returns*/])
247 hw_memory_device
*hw_memory
= device_instance_data(instance
);
248 device
*me
= device_instance_device(instance
);
250 unsigned_word alignment
;
252 unsigned_cell address
;
253 hw_memory_chunk
*chunk
= NULL
;
255 /* get the alignment from the stack */
256 if (n_stack_args
< stackp
+ 1)
257 device_error(me
, "claim - incorrect number of arguments (alignment missing)");
258 alignment
= stack_args
[stackp
];
261 /* get the size from the stack */
264 int nr_cells
= device_nr_size_cells(device_parent(me
));
265 if (n_stack_args
< stackp
+ nr_cells
)
266 device_error(me
, "claim - incorrect number of arguments (size missing)");
267 for (i
= 0; i
< nr_cells
- 1; i
++) {
268 if (stack_args
[stackp
] != 0)
269 device_error(me
, "claim - multi-cell sizes not supported");
272 size
= stack_args
[stackp
];
276 /* get the address from the stack */
278 int nr_cells
= device_nr_address_cells(device_parent(me
));
279 if (alignment
!= 0) {
280 if (n_stack_args
!= stackp
) {
281 if (n_stack_args
== stackp
+ nr_cells
)
282 DTRACE(memory
, ("claim - extra address argument ignored\n"));
284 device_error(me
, "claim - incorrect number of arguments (optional addr)");
290 if (n_stack_args
!= stackp
+ nr_cells
)
291 device_error(me
, "claim - incorrect number of arguments (addr missing)");
292 for (i
= 0; i
< nr_cells
- 1; i
++) {
293 if (stack_args
[stackp
] != 0)
294 device_error(me
, "claim - multi-cell addresses not supported");
297 address
= stack_args
[stackp
];
301 /* check that there is space for the result */
302 if (n_stack_returns
!= 0
303 && n_stack_returns
!= device_nr_address_cells(device_parent(me
)))
304 device_error(me
, "claim - invalid number of return arguments");
306 /* find a chunk candidate, either according to address or alignment */
307 if (alignment
== 0) {
308 chunk
= hw_memory
->heap
;
309 while (chunk
!= NULL
) {
310 if ((address
+ size
) <= (chunk
->address
+ chunk
->size
))
314 if (chunk
== NULL
|| address
< chunk
->address
|| !chunk
->available
)
315 device_error(me
, "failed to allocate %ld bytes at 0x%lx",
316 (unsigned long)size
, (unsigned long)address
);
317 DTRACE(memory
, ("claim - address=0x%lx size=0x%lx\n",
318 (unsigned long)address
,
319 (unsigned long)size
));
322 /* adjust the alignment so that it is a power of two */
323 unsigned_word align_mask
= 1;
324 while (align_mask
< alignment
&& align_mask
!= 0)
327 device_error(me
, "alignment 0x%lx is to large", (unsigned long)alignment
);
329 /* now find an aligned chunk that fits */
330 chunk
= hw_memory
->heap
;
331 while (chunk
!= NULL
) {
332 address
= ((chunk
->address
+ align_mask
) & ~align_mask
);
333 if ((chunk
->available
)
334 && (chunk
->address
+ chunk
->size
>= address
+ size
))
339 device_error(me
, "failed to allocate %ld bytes with alignment %ld",
340 (unsigned long)size
, (unsigned long)alignment
);
341 DTRACE(memory
, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
343 (unsigned long)alignment
,
344 (unsigned long)alignment
,
345 (unsigned long)address
));
348 /* break off a bit before this chunk if needed */
349 ASSERT(address
>= chunk
->address
);
350 if (address
> chunk
->address
) {
351 hw_memory_chunk
*next_chunk
= ZALLOC(hw_memory_chunk
);
352 /* insert a new chunk */
353 next_chunk
->next
= chunk
->next
;
354 chunk
->next
= next_chunk
;
355 /* adjust the address/size */
356 next_chunk
->address
= address
;
357 next_chunk
->size
= chunk
->address
+ chunk
->size
- next_chunk
->address
;
358 next_chunk
->available
= 1;
359 chunk
->size
= next_chunk
->address
- chunk
->address
;
360 /* make this new chunk the one to allocate */
363 ASSERT(address
== chunk
->address
);
365 /* break off a bit after this chunk if needed */
366 ASSERT(address
+ size
<= chunk
->address
+ chunk
->size
);
367 if (address
+ size
< chunk
->address
+ chunk
->size
) {
368 hw_memory_chunk
*next_chunk
= ZALLOC(hw_memory_chunk
);
369 /* insert it in to the list */
370 next_chunk
->next
= chunk
->next
;
371 chunk
->next
= next_chunk
;
372 /* adjust the address/size */
373 next_chunk
->address
= address
+ size
;
374 next_chunk
->size
= chunk
->address
+ chunk
->size
- next_chunk
->address
;
375 next_chunk
->available
= 1;
376 chunk
->size
= next_chunk
->address
- chunk
->address
;
378 ASSERT(address
+ size
== chunk
->address
+ chunk
->size
);
380 /* now allocate/return it */
381 chunk
->available
= 0;
382 hw_memory_set_available(device_instance_device(instance
), hw_memory
);
383 if (n_stack_returns
> 0) {
385 for (i
= 0; i
< n_stack_returns
- 1; i
++)
386 stack_returns
[i
] = 0;
387 stack_returns
[n_stack_returns
- 1] = address
;
395 hw_memory_instance_release(device_instance
*instance
,
397 unsigned_cell stack_args
[/*n_stack_args*/],
399 unsigned_cell stack_returns
[/*n_stack_returns*/])
401 hw_memory_device
*hw_memory
= device_instance_data(instance
);
402 device
*me
= device_instance_device(instance
);
403 unsigned_word length
;
404 unsigned_word address
;
406 hw_memory_chunk
*chunk
;
408 /* get the length from the stack */
411 int nr_cells
= device_nr_size_cells(device_parent(me
));
412 if (n_stack_args
< stackp
+ nr_cells
)
413 device_error(me
, "release - incorrect number of arguments (length missing)");
414 for (i
= 0; i
< nr_cells
- 1; i
++) {
415 if (stack_args
[stackp
] != 0)
416 device_error(me
, "release - multi-cell length not supported");
419 length
= stack_args
[stackp
];
423 /* get the address from the stack */
426 int nr_cells
= device_nr_address_cells(device_parent(me
));
427 if (n_stack_args
!= stackp
+ nr_cells
)
428 device_error(me
, "release - incorrect number of arguments (addr missing)");
429 for (i
= 0; i
< nr_cells
- 1; i
++) {
430 if (stack_args
[stackp
] != 0)
431 device_error(me
, "release - multi-cell addresses not supported");
434 address
= stack_args
[stackp
];
438 if (n_stack_returns
!= 0)
439 device_error(me
, "release - nonzero number of results");
441 /* try to free the corresponding memory chunk */
442 chunk
= hw_memory
->heap
;
443 while (chunk
!= NULL
) {
444 if (chunk
->address
== address
445 && chunk
->size
== length
) {
447 if (chunk
->available
)
448 device_error(me
, "memory chunk 0x%lx (size 0x%lx) already available",
449 (unsigned long)address
,
450 (unsigned long)length
);
452 /* free this chunk */
453 DTRACE(memory
, ("release - address=0x%lx, length=0x%lx\n",
454 (unsigned long) address
,
455 (unsigned long) length
));
456 chunk
->available
= 1;
460 else if (chunk
->address
>= address
461 && chunk
->address
+ chunk
->size
<= address
+ length
) {
463 if (!chunk
->available
) {
464 DTRACE(memory
, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
465 (unsigned long) chunk
->address
,
466 (unsigned long) chunk
->size
,
467 (unsigned long) address
,
468 (unsigned long) length
));
469 chunk
->available
= 1;
475 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
476 (unsigned long)address
,
477 (unsigned long)(address
+ length
- 1));
480 /* check for the chance to merge two adjacent available memory chunks */
481 chunk
= hw_memory
->heap
;
482 while (chunk
!= NULL
) {
484 && chunk
->next
!= NULL
&& chunk
->next
->available
) {
486 hw_memory_chunk
*delete = chunk
->next
;
487 ASSERT(chunk
->address
+ chunk
->size
== delete->address
);
488 chunk
->size
+= delete->size
;
489 chunk
->next
= delete->next
;
497 /* update the corresponding property */
498 hw_memory_set_available(device_instance_device(instance
), hw_memory
);
504 static device_instance_methods hw_memory_instance_methods
[] = {
505 { "claim", hw_memory_instance_claim
},
506 { "release", hw_memory_instance_release
},
510 static device_instance_callbacks
const hw_memory_instance_callbacks
= {
511 hw_memory_instance_delete
,
512 NULL
/*read*/, NULL
/*write*/, NULL
/*seek*/,
513 hw_memory_instance_methods
516 static device_instance
*
517 hw_memory_create_instance(device
*me
,
521 return device_create_instance_from(me
, NULL
,
522 device_data(me
), /* nothing better */
524 &hw_memory_instance_callbacks
);
527 static device_callbacks
const hw_memory_callbacks
= {
528 { hw_memory_init_address
, },
529 { NULL
, }, /* address */
532 { NULL
, }, /* interrupt */
533 { NULL
, }, /* unit */
534 hw_memory_create_instance
,
537 const device_descriptor hw_memory_device_descriptor
[] = {
538 { "memory", hw_memory_create
, &hw_memory_callbacks
},
542 #endif /* _HW_MEMORY_C_ */