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 2 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, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #ifndef STATIC_INLINE_HW_MEMORY
26 #define STATIC_INLINE_HW_MEMORY STATIC_INLINE
29 #include "device_table.h"
34 memory - description of system memory
40 This device describes the size and location of the banks of
41 physical memory within the simulation.
43 In addition, this device supports the "claim" and "release" methods
44 that can be used by OpenBoot client programs to manage the
45 allocation of physical memory.
51 reg = { <address> <size> } (required)
53 Each pair specify one bank of memory.
55 available = { <address> <size> } (automatic)
57 Each pair specifies a block of memory that is currently unallocated.
63 OpenFirmware doesn't make it clear if, when releasing memory the
64 same address + size pair as was used during the claim should be
67 It is assumed that #size-cells and #address-cells for the parent
68 node of this device are both one i.e. an address or size can be
69 specified using a single memory cell (word).
71 Significant work will be required before the <<memory>> device can
72 support 64bit addresses (#address-cells equal two).
76 typedef struct _memory_reg_spec {
81 typedef struct _hw_memory_chunk hw_memory_chunk;
82 struct _hw_memory_chunk {
83 unsigned_word address;
86 hw_memory_chunk *next;
89 typedef struct _hw_memory_device {
90 hw_memory_chunk *heap;
95 hw_memory_create(const char *name,
96 const device_unit *unit_address,
99 hw_memory_device *hw_memory = ZALLOC(hw_memory_device);
105 hw_memory_set_available(device *me,
106 hw_memory_device *hw_memory)
108 hw_memory_chunk *chunk = NULL;
109 memory_reg_spec *available = NULL;
110 int nr_available = 0;
112 int sizeof_available = 0;
113 /* determine the nr of available chunks */
114 chunk = hw_memory->heap;
116 while (chunk != NULL) {
117 if (chunk->available)
119 ASSERT(chunk->next == NULL
120 || chunk->address < chunk->next->address);
121 ASSERT(chunk->next == NULL
122 || chunk->address + chunk->size == chunk->next->address);
125 /* now create the available struct */
126 ASSERT(nr_available > 0);
127 sizeof_available = sizeof(memory_reg_spec) * nr_available;
128 available = zalloc(sizeof_available);
129 chunk = hw_memory->heap;
131 while (chunk != NULL) {
132 if (chunk->available) {
133 available[curr].base = H2BE_cell(chunk->address);
134 available[curr].size = H2BE_cell(chunk->size);
140 device_set_array_property(me, "available", available, sizeof_available);
146 hw_memory_init_address(device *me)
148 hw_memory_device *hw_memory = (hw_memory_device*)device_data(me);
150 /* free up any previous structures */
152 hw_memory_chunk *curr_chunk = hw_memory->heap;
153 hw_memory->heap = NULL;
154 while (curr_chunk != NULL) {
155 hw_memory_chunk *dead_chunk = curr_chunk;
156 curr_chunk = dead_chunk->next;
157 dead_chunk->next = NULL;
162 /* attach memory regions according to the "reg" property */
165 reg_property_spec reg;
167 device_find_reg_array_property(me, "reg", reg_nr, ®);
170 /* check that the entry meets restrictions */
171 for (i = 0; i < reg.address.nr_cells - 1; i++)
172 if (reg.address.cells[i] != 0)
173 device_error(me, "Only single celled addresses supported");
174 for (i = 0; i < reg.size.nr_cells - 1; i++)
175 if (reg.size.cells[i] != 0)
176 device_error(me, "Only single celled sizes supported");
177 /* attach the range */
178 device_attach_address(device_parent(me),
181 reg.address.cells[reg.address.nr_cells - 1],
182 reg.size.cells[reg.size.nr_cells - 1],
183 access_read_write_exec,
188 /* create the initial `available memory' data structure */
189 if (device_find_property(me, "available") != NULL) {
190 hw_memory_chunk **curr_chunk = &hw_memory->heap;
193 int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy);
194 if ((nr_cells % 2) != 0)
195 device_error(me, "property \"available\" invalid - contains an odd number of cells");
199 hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk);
200 device_find_integer_array_property(me, "available", cell_nr,
201 &new_chunk->address);
202 device_find_integer_array_property(me, "available", cell_nr + 1,
204 new_chunk->available = 1;
205 *curr_chunk = new_chunk;
206 curr_chunk = &new_chunk->next;
210 hw_memory_chunk **curr_chunk = &hw_memory->heap;
212 reg_property_spec reg;
214 device_find_reg_array_property(me, "reg", reg_nr, ®);
216 hw_memory_chunk *new_chunk;
217 new_chunk = ZALLOC(hw_memory_chunk);
218 new_chunk->address = reg.address.cells[reg.address.nr_cells - 1];
219 new_chunk->size = reg.size.cells[reg.size.nr_cells - 1];
220 new_chunk->available = 1;
221 *curr_chunk = new_chunk;
222 curr_chunk = &new_chunk->next;
226 /* initialize the alloc property for this device */
227 hw_memory_set_available(me, hw_memory);
231 hw_memory_instance_delete(device_instance *instance)
237 hw_memory_instance_claim(device_instance *instance,
239 unsigned_cell stack_args[/*n_stack_args*/],
241 unsigned_cell stack_returns[/*n_stack_returns*/])
243 hw_memory_device *hw_memory = device_instance_data(instance);
244 device *me = device_instance_device(instance);
246 unsigned_word alignment;
248 unsigned_cell address;
249 hw_memory_chunk *chunk = NULL;
251 /* get the alignment from the stack */
252 if (n_stack_args < stackp + 1)
253 device_error(me, "claim - incorrect number of arguments (alignment missing)");
254 alignment = stack_args[stackp];
257 /* get the size from the stack */
260 int nr_cells = device_nr_size_cells(device_parent(me));
261 if (n_stack_args < stackp + nr_cells)
262 device_error(me, "claim - incorrect number of arguments (size missing)");
263 for (i = 0; i < nr_cells - 1; i++) {
264 if (stack_args[stackp] != 0)
265 device_error(me, "claim - multi-cell sizes not supported");
268 size = stack_args[stackp];
272 /* get the address from the stack */
274 int nr_cells = device_nr_address_cells(device_parent(me));
275 if (alignment != 0) {
276 if (n_stack_args != stackp) {
277 if (n_stack_args == stackp + nr_cells)
278 DTRACE(memory, ("claim - extra address argument ignored\n"));
280 device_error(me, "claim - incorrect number of arguments (optional addr)");
286 if (n_stack_args != stackp + nr_cells)
287 device_error(me, "claim - incorrect number of arguments (addr missing)");
288 for (i = 0; i < nr_cells - 1; i++) {
289 if (stack_args[stackp] != 0)
290 device_error(me, "claim - multi-cell addresses not supported");
293 address = stack_args[stackp];
297 /* check that there is space for the result */
298 if (n_stack_returns != 0
299 && n_stack_returns != device_nr_address_cells(device_parent(me)))
300 device_error(me, "claim - invalid number of return arguments");
302 /* find a chunk candidate, either according to address or alignment */
303 if (alignment == 0) {
304 chunk = hw_memory->heap;
305 while (chunk != NULL) {
306 if ((address + size) <= (chunk->address + chunk->size))
310 if (chunk == NULL || address < chunk->address || !chunk->available)
311 device_error(me, "failed to allocate %ld bytes at 0x%lx",
312 (unsigned long)size, (unsigned long)address);
313 DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n",
314 (unsigned long)address,
315 (unsigned long)size));
318 /* adjust the alignment so that it is a power of two */
319 unsigned_word align_mask = 1;
320 while (align_mask < alignment && align_mask != 0)
323 device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment);
325 /* now find an aligned chunk that fits */
326 chunk = hw_memory->heap;
327 while (chunk != NULL) {
328 address = ((chunk->address + align_mask) & ~align_mask);
329 if ((chunk->available)
330 && (chunk->address + chunk->size >= address + size))
335 device_error(me, "failed to allocate %ld bytes with alignment %ld",
336 (unsigned long)size, (unsigned long)alignment);
337 DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
339 (unsigned long)alignment,
340 (unsigned long)alignment,
341 (unsigned long)address));
344 /* break off a bit before this chunk if needed */
345 ASSERT(address >= chunk->address);
346 if (address > chunk->address) {
347 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
348 /* insert a new chunk */
349 next_chunk->next = chunk->next;
350 chunk->next = next_chunk;
351 /* adjust the address/size */
352 next_chunk->address = address;
353 next_chunk->size = chunk->address + chunk->size - next_chunk->address;
354 next_chunk->available = 1;
355 chunk->size = next_chunk->address - chunk->address;
356 /* make this new chunk the one to allocate */
359 ASSERT(address == chunk->address);
361 /* break off a bit after this chunk if needed */
362 ASSERT(address + size <= chunk->address + chunk->size);
363 if (address + size < chunk->address + chunk->size) {
364 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
365 /* insert it in to the list */
366 next_chunk->next = chunk->next;
367 chunk->next = next_chunk;
368 /* adjust the address/size */
369 next_chunk->address = address + size;
370 next_chunk->size = chunk->address + chunk->size - next_chunk->address;
371 next_chunk->available = 1;
372 chunk->size = next_chunk->address - chunk->address;
374 ASSERT(address + size == chunk->address + chunk->size);
376 /* now allocate/return it */
377 chunk->available = 0;
378 hw_memory_set_available(device_instance_device(instance), hw_memory);
379 if (n_stack_returns > 0) {
381 for (i = 0; i < n_stack_returns - 1; i++)
382 stack_returns[i] = 0;
383 stack_returns[n_stack_returns - 1] = address;
391 hw_memory_instance_release(device_instance *instance,
393 unsigned_cell stack_args[/*n_stack_args*/],
395 unsigned_cell stack_returns[/*n_stack_returns*/])
397 hw_memory_device *hw_memory = device_instance_data(instance);
398 device *me = device_instance_device(instance);
399 unsigned_word length;
400 unsigned_word address;
402 hw_memory_chunk *chunk;
404 /* get the length from the stack */
407 int nr_cells = device_nr_size_cells(device_parent(me));
408 if (n_stack_args < stackp + nr_cells)
409 device_error(me, "release - incorrect number of arguments (length missing)");
410 for (i = 0; i < nr_cells - 1; i++) {
411 if (stack_args[stackp] != 0)
412 device_error(me, "release - multi-cell length not supported");
415 length = stack_args[stackp];
419 /* get the address from the stack */
422 int nr_cells = device_nr_address_cells(device_parent(me));
423 if (n_stack_args != stackp + nr_cells)
424 device_error(me, "release - incorrect number of arguments (addr missing)");
425 for (i = 0; i < nr_cells - 1; i++) {
426 if (stack_args[stackp] != 0)
427 device_error(me, "release - multi-cell addresses not supported");
430 address = stack_args[stackp];
434 if (n_stack_returns != 0)
435 device_error(me, "release - nonzero number of results");
437 /* try to free the corresponding memory chunk */
438 chunk = hw_memory->heap;
439 while (chunk != NULL) {
440 if (chunk->address == address
441 && chunk->size == length) {
443 if (chunk->available)
444 device_error(me, "memory chunk 0x%lx (size 0x%lx) already available",
445 (unsigned long)address,
446 (unsigned long)length);
448 /* free this chunk */
449 DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n",
450 (unsigned long) address,
451 (unsigned long) length));
452 chunk->available = 1;
456 else if (chunk->address >= address
457 && chunk->address + chunk->size <= address + length) {
459 if (!chunk->available) {
460 DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
461 (unsigned long) chunk->address,
462 (unsigned long) chunk->size,
463 (unsigned long) address,
464 (unsigned long) length));
465 chunk->available = 1;
471 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
472 (unsigned long)address,
473 (unsigned long)(address + length - 1));
476 /* check for the chance to merge two adjacent available memory chunks */
477 chunk = hw_memory->heap;
478 while (chunk != NULL) {
480 && chunk->next != NULL && chunk->next->available) {
482 hw_memory_chunk *delete = chunk->next;
483 ASSERT(chunk->address + chunk->size == delete->address);
484 chunk->size += delete->size;
485 chunk->next = delete->next;
493 /* update the corresponding property */
494 hw_memory_set_available(device_instance_device(instance), hw_memory);
500 static device_instance_methods hw_memory_instance_methods[] = {
501 { "claim", hw_memory_instance_claim },
502 { "release", hw_memory_instance_release },
506 static device_instance_callbacks const hw_memory_instance_callbacks = {
507 hw_memory_instance_delete,
508 NULL /*read*/, NULL /*write*/, NULL /*seek*/,
509 hw_memory_instance_methods
512 static device_instance *
513 hw_memory_create_instance(device *me,
517 return device_create_instance_from(me, NULL,
518 device_data(me), /* nothing better */
520 &hw_memory_instance_callbacks);
523 static device_callbacks const hw_memory_callbacks = {
524 { hw_memory_init_address, },
525 { NULL, }, /* address */
528 { NULL, }, /* interrupt */
529 { NULL, }, /* unit */
530 hw_memory_create_instance,
533 const device_descriptor hw_memory_device_descriptor[] = {
534 { "memory", hw_memory_create, &hw_memory_callbacks },
538 #endif /* _HW_MEMORY_C_ */