Minor spelling fix in ChangeLog.
[external/binutils.git] / sim / ppc / hw_memory.c
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
76 typedef struct _memory_reg_spec {
77   unsigned_cell base;
78   unsigned_cell size;
79 } memory_reg_spec;
80
81 typedef struct _hw_memory_chunk hw_memory_chunk;
82 struct _hw_memory_chunk {
83   unsigned_word address;
84   unsigned_word size;
85   int available;
86   hw_memory_chunk *next;
87 };
88
89 typedef struct _hw_memory_device {
90   hw_memory_chunk *heap;
91 } hw_memory_device;
92
93
94 static void *
95 hw_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
104 static void
105 hw_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   free(available);
142 }
143
144
145 static void
146 hw_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       free(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
230 static void
231 hw_memory_instance_delete(device_instance *instance)
232 {
233   return;
234 }
235
236 static int
237 hw_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
390 static int
391 hw_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       free(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
500 static device_instance_methods hw_memory_instance_methods[] = {
501   { "claim", hw_memory_instance_claim },
502   { "release", hw_memory_instance_release },
503   { NULL, },
504 };
505
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
510 };
511
512 static device_instance *
513 hw_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
523 static 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
533 const device_descriptor hw_memory_device_descriptor[] = {
534   { "memory", hw_memory_create, &hw_memory_callbacks },
535   { NULL },
536 };
537
538 #endif /* _HW_MEMORY_C_ */