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 2 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, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19    */
20
21
22#ifndef _HW_MEMORY_C_
23#define _HW_MEMORY_C_
24
25#ifndef STATIC_INLINE_HW_MEMORY
26#define STATIC_INLINE_HW_MEMORY STATIC_INLINE
27#endif
28
29#include "device_table.h"
30
31/* DEVICE
32
33
34   memory - description of system memory
35
36
37   DESCRIPTION
38
39
40   This device describes the size and location of the banks of
41   physical memory within the simulation.
42
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.
46
47
48   PROPERTIES
49
50
51   reg = { <address> <size> } (required)
52
53   Each pair specify one bank of memory.
54
55   available = { <address> <size> } (automatic)
56
57   Each pair specifies a block of memory that is currently unallocated.
58
59
60   BUGS
61
62
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
65   specified.
66
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).
70
71   Significant work will be required before the <<memory>> device can
72   support 64bit addresses (#address-cells equal two).
73
74   */
75
76typedef struct _memory_reg_spec {
77  unsigned_cell base;
78  unsigned_cell size;
79} memory_reg_spec;
80
81typedef struct _hw_memory_chunk hw_memory_chunk;
82struct _hw_memory_chunk {
83  unsigned_word address;
84  unsigned_word size;
85  int available;
86  hw_memory_chunk *next;
87};
88
89typedef struct _hw_memory_device {
90  hw_memory_chunk *heap;
91} hw_memory_device;
92
93
94static void *
95hw_memory_create(const char *name,
96		 const device_unit *unit_address,
97		 const char *args)
98{
99  hw_memory_device *hw_memory = ZALLOC(hw_memory_device);
100  return hw_memory;
101}
102
103
104static void
105hw_memory_set_available(device *me,
106			hw_memory_device *hw_memory)
107{
108  hw_memory_chunk *chunk = NULL;
109  memory_reg_spec *available = NULL;
110  int nr_available = 0;
111  int curr = 0;
112  int sizeof_available = 0;
113  /* determine the nr of available chunks */
114  chunk = hw_memory->heap;
115  nr_available = 0;
116  while (chunk != NULL) {
117    if (chunk->available)
118      nr_available += 1;
119    ASSERT(chunk->next == NULL
120	   || chunk->address < chunk->next->address);
121    ASSERT(chunk->next == NULL
122	   || chunk->address + chunk->size == chunk->next->address);
123    chunk = chunk->next;
124  }
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;
130  curr = 0;
131  while (chunk != NULL) {
132    if (chunk->available) {
133      available[curr].base = H2BE_cell(chunk->address);
134      available[curr].size = H2BE_cell(chunk->size);
135      curr += 1;
136    }
137    chunk = chunk->next;
138  }
139  /* update */
140  device_set_array_property(me, "available", available, sizeof_available);
141  zfree(available);
142}
143
144
145static void
146hw_memory_init_address(device *me)
147{
148  hw_memory_device *hw_memory = (hw_memory_device*)device_data(me);
149
150  /* free up any previous structures */
151  {
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;
158      zfree(dead_chunk);
159    }
160  }
161
162  /* attach memory regions according to the "reg" property */
163  {
164    int reg_nr;
165    reg_property_spec reg;
166    for (reg_nr = 0;
167	 device_find_reg_array_property(me, "reg", reg_nr, &reg);
168	 reg_nr++) {
169      int i;
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),
179			    attach_raw_memory,
180			    0 /*address space*/,
181			    reg.address.cells[reg.address.nr_cells - 1],
182			    reg.size.cells[reg.size.nr_cells - 1],
183			    access_read_write_exec,
184			    me);
185    }
186  }
187
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;
191    int cell_nr;
192    unsigned_cell dummy;
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");
196    for (cell_nr = 0;
197	 cell_nr < nr_cells;
198	 cell_nr += 2) {
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,
203					 &new_chunk->size);
204      new_chunk->available = 1;
205      *curr_chunk = new_chunk;
206      curr_chunk = &new_chunk->next;
207    }
208  }
209  else {
210    hw_memory_chunk **curr_chunk = &hw_memory->heap;
211    int reg_nr;
212    reg_property_spec reg;
213    for (reg_nr = 0;
214	 device_find_reg_array_property(me, "reg", reg_nr, &reg);
215	 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;
223    }
224  }
225
226  /* initialize the alloc property for this device */
227  hw_memory_set_available(me, hw_memory);
228}
229
230static void
231hw_memory_instance_delete(device_instance *instance)
232{
233  return;
234}
235
236static int
237hw_memory_instance_claim(device_instance *instance,
238			 int n_stack_args,
239			 unsigned_cell stack_args[/*n_stack_args*/],
240			 int n_stack_returns,
241			 unsigned_cell stack_returns[/*n_stack_returns*/])
242{
243  hw_memory_device *hw_memory = device_instance_data(instance);
244  device *me = device_instance_device(instance);
245  int stackp = 0;
246  unsigned_word alignment;
247  unsigned_cell size;
248  unsigned_cell address;
249  hw_memory_chunk *chunk = NULL;
250
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];
255  stackp++;
256
257  /* get the size from the stack */
258  {
259    int i;
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");
266      stackp++;
267    }
268    size = stack_args[stackp];
269    stackp++;
270  }
271
272  /* get the address from the stack */
273  {
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"));
279	else
280	  device_error(me, "claim - incorrect number of arguments (optional addr)");
281      }
282      address = 0;
283    }
284    else {
285      int i;
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");
291	stackp++;
292      }
293      address = stack_args[stackp];
294    }
295  }
296
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");
301
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))
307	break;
308      chunk = chunk->next;
309    }
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));
316  }
317  else {
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)
321      align_mask <<= 1;
322    if (align_mask == 0)
323      device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment);
324    align_mask -= 1;
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))
331	break;
332      chunk = chunk->next;
333    }
334    if (chunk == NULL)
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",
338		    (unsigned long)size,
339		    (unsigned long)alignment,
340		    (unsigned long)alignment,
341		    (unsigned long)address));
342  }
343
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 */
357    chunk = next_chunk;
358  }
359  ASSERT(address == chunk->address);
360
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;
373  }
374  ASSERT(address + size == chunk->address + chunk->size);
375
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) {
380    int i;
381    for (i = 0; i < n_stack_returns - 1; i++)
382      stack_returns[i] = 0;
383    stack_returns[n_stack_returns - 1] = address;
384  }
385
386  return 0;
387}
388
389
390static int
391hw_memory_instance_release(device_instance *instance,
392			   int n_stack_args,
393			   unsigned_cell stack_args[/*n_stack_args*/],
394			   int n_stack_returns,
395			   unsigned_cell stack_returns[/*n_stack_returns*/])
396{
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;
401  int stackp = 0;
402  hw_memory_chunk *chunk;
403
404  /* get the length from the stack */
405  {
406    int i;
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");
413      stackp++;
414    }
415    length = stack_args[stackp];
416    stackp++;
417  }
418
419  /* get the address from the stack */
420  {
421    int i;
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");
428      stackp++;
429    }
430    address = stack_args[stackp];
431  }
432
433  /* returns ok */
434  if (n_stack_returns != 0)
435    device_error(me, "release - nonzero number of results");
436
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) {
442      /* an exact match */
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);
447      else {
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;
453	break;
454      }
455    }
456    else if (chunk->address >= address
457	     && chunk->address + chunk->size <= address + length) {
458      /* a sub region */
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;
466      }
467    }
468    chunk = chunk->next;
469  }
470  if (chunk == NULL) {
471    printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
472		    (unsigned long)address,
473		    (unsigned long)(address + length - 1));
474  }
475
476  /* check for the chance to merge two adjacent available memory chunks */
477  chunk = hw_memory->heap;
478  while (chunk != NULL) {
479    if (chunk->available
480	&& chunk->next != NULL && chunk->next->available) {
481      /* adjacent */
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;
486      zfree(delete);
487    }
488    else {
489      chunk = chunk->next;
490    }
491  }
492
493  /* update the corresponding property */
494  hw_memory_set_available(device_instance_device(instance), hw_memory);
495
496  return 0;
497}
498
499
500static device_instance_methods hw_memory_instance_methods[] = {
501  { "claim", hw_memory_instance_claim },
502  { "release", hw_memory_instance_release },
503  { NULL, },
504};
505
506static 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
510};
511
512static device_instance *
513hw_memory_create_instance(device *me,
514			  const char *path,
515			  const char *args)
516{
517  return device_create_instance_from(me, NULL,
518				     device_data(me), /* nothing better */
519				     path, args,
520				     &hw_memory_instance_callbacks);
521}
522
523static device_callbacks const hw_memory_callbacks = {
524  { hw_memory_init_address, },
525  { NULL, }, /* address */
526  { NULL, }, /* IO */
527  { NULL, }, /* DMA */
528  { NULL, }, /* interrupt */
529  { NULL, }, /* unit */
530  hw_memory_create_instance,
531};
532
533const device_descriptor hw_memory_device_descriptor[] = {
534  { "memory", hw_memory_create, &hw_memory_callbacks },
535  { NULL },
536};
537
538#endif /* _HW_MEMORY_C_ */
539