sim: constify prog_name
[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 3 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, see <http://www.gnu.org/licenses/>.
17  
18     */
19
20
21 #ifndef _HW_MEMORY_C_
22 #define _HW_MEMORY_C_
23
24 #ifndef STATIC_INLINE_HW_MEMORY
25 #define STATIC_INLINE_HW_MEMORY STATIC_INLINE
26 #endif
27
28 #include "device_table.h"
29
30 /* DEVICE
31
32
33    memory - description of system memory
34
35
36    DESCRIPTION
37
38
39    This device describes the size and location of the banks of
40    physical memory within the simulation.
41
42    In addition, this device supports the "claim" and "release" methods
43    that can be used by OpenBoot client programs to manage the
44    allocation of physical memory.
45
46
47    PROPERTIES
48
49
50    reg = { <address> <size> } (required)
51
52    Each pair specify one bank of memory.
53
54    available = { <address> <size> } (automatic)
55
56    Each pair specifies a block of memory that is currently unallocated.  
57
58
59    BUGS
60
61
62    OpenFirmware doesn't make it clear if, when releasing memory the
63    same address + size pair as was used during the claim should be
64    specified.
65
66    It is assumed that #size-cells and #address-cells for the parent
67    node of this device are both one i.e. an address or size can be
68    specified using a single memory cell (word).
69
70    Significant work will be required before the <<memory>> device can
71    support 64bit addresses (#address-cells equal two).
72
73    */
74
75 typedef struct _memory_reg_spec {
76   unsigned_cell base;
77   unsigned_cell size;
78 } memory_reg_spec;
79
80 typedef struct _hw_memory_chunk hw_memory_chunk;
81 struct _hw_memory_chunk {
82   unsigned_word address;
83   unsigned_word size;
84   int available;
85   hw_memory_chunk *next;
86 };
87
88 typedef struct _hw_memory_device {
89   hw_memory_chunk *heap;
90 } hw_memory_device;
91
92
93 static void *
94 hw_memory_create(const char *name,
95                  const device_unit *unit_address,
96                  const char *args)
97 {
98   hw_memory_device *hw_memory = ZALLOC(hw_memory_device);
99   return hw_memory;
100 }
101
102
103 static void
104 hw_memory_set_available(device *me,
105                         hw_memory_device *hw_memory)
106 {
107   hw_memory_chunk *chunk = NULL;
108   memory_reg_spec *available = NULL;
109   int nr_available = 0;
110   int curr = 0;
111   int sizeof_available = 0;
112   /* determine the nr of available chunks */
113   chunk = hw_memory->heap;
114   nr_available = 0;
115   while (chunk != NULL) {
116     if (chunk->available)
117       nr_available += 1;
118     ASSERT(chunk->next == NULL
119            || chunk->address < chunk->next->address);
120     ASSERT(chunk->next == NULL
121            || chunk->address + chunk->size == chunk->next->address);
122     chunk = chunk->next;
123   }
124   /* now create the available struct */
125   ASSERT(nr_available > 0);
126   sizeof_available = sizeof(memory_reg_spec) * nr_available;
127   available = zalloc(sizeof_available);
128   chunk = hw_memory->heap;
129   curr = 0;
130   while (chunk != NULL) {
131     if (chunk->available) {
132       available[curr].base = H2BE_cell(chunk->address);
133       available[curr].size = H2BE_cell(chunk->size);
134       curr += 1;
135     }
136     chunk = chunk->next;
137   }
138   /* update */
139   device_set_array_property(me, "available", available, sizeof_available);
140   free(available);
141 }
142
143
144 static void
145 hw_memory_init_address(device *me)
146 {
147   hw_memory_device *hw_memory = (hw_memory_device*)device_data(me);
148
149   /* free up any previous structures */
150   {
151     hw_memory_chunk *curr_chunk = hw_memory->heap;
152     hw_memory->heap = NULL;
153     while (curr_chunk != NULL) {
154       hw_memory_chunk *dead_chunk = curr_chunk;
155       curr_chunk = dead_chunk->next;
156       dead_chunk->next = NULL;
157       free(dead_chunk);
158     }
159   }
160
161   /* attach memory regions according to the "reg" property */
162   {
163     int reg_nr;
164     reg_property_spec reg;
165     for (reg_nr = 0;
166          device_find_reg_array_property(me, "reg", reg_nr, &reg);
167          reg_nr++) {
168       int i;
169       /* check that the entry meets restrictions */
170       for (i = 0; i < reg.address.nr_cells - 1; i++)
171         if (reg.address.cells[i] != 0)
172           device_error(me, "Only single celled addresses supported");
173       for (i = 0; i < reg.size.nr_cells - 1; i++)
174         if (reg.size.cells[i] != 0)
175           device_error(me, "Only single celled sizes supported");
176       /* attach the range */
177       device_attach_address(device_parent(me),
178                             attach_raw_memory,
179                             0 /*address space*/,
180                             reg.address.cells[reg.address.nr_cells - 1],
181                             reg.size.cells[reg.size.nr_cells - 1],
182                             access_read_write_exec,
183                             me);
184     }
185   }
186
187   /* create the initial `available memory' data structure */
188   if (device_find_property(me, "available") != NULL) {
189     hw_memory_chunk **curr_chunk = &hw_memory->heap;
190     int cell_nr;
191     unsigned_cell dummy;
192     int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy);
193     if ((nr_cells % 2) != 0)
194       device_error(me, "property \"available\" invalid - contains an odd number of cells");
195     for (cell_nr = 0;
196          cell_nr < nr_cells;
197          cell_nr += 2) {
198       hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk);
199       device_find_integer_array_property(me, "available", cell_nr,
200                                          &new_chunk->address);
201       device_find_integer_array_property(me, "available", cell_nr + 1,
202                                          &new_chunk->size);
203       new_chunk->available = 1;
204       *curr_chunk = new_chunk;
205       curr_chunk = &new_chunk->next;
206     }
207   }
208   else {
209     hw_memory_chunk **curr_chunk = &hw_memory->heap;
210     int reg_nr;
211     reg_property_spec reg;
212     for (reg_nr = 0;
213          device_find_reg_array_property(me, "reg", reg_nr, &reg);
214          reg_nr++) {
215       hw_memory_chunk *new_chunk;
216       new_chunk = ZALLOC(hw_memory_chunk);
217       new_chunk->address = reg.address.cells[reg.address.nr_cells - 1];
218       new_chunk->size = reg.size.cells[reg.size.nr_cells - 1];
219       new_chunk->available = 1;
220       *curr_chunk = new_chunk;
221       curr_chunk = &new_chunk->next;
222     }
223   }
224
225   /* initialize the alloc property for this device */
226   hw_memory_set_available(me, hw_memory);
227 }
228
229 static void
230 hw_memory_instance_delete(device_instance *instance)
231 {
232   return;
233 }
234
235 static int
236 hw_memory_instance_claim(device_instance *instance,
237                          int n_stack_args,
238                          unsigned_cell stack_args[/*n_stack_args*/],
239                          int n_stack_returns,
240                          unsigned_cell stack_returns[/*n_stack_returns*/])
241 {
242   hw_memory_device *hw_memory = device_instance_data(instance);
243   device *me = device_instance_device(instance);
244   int stackp = 0;
245   unsigned_word alignment;
246   unsigned_cell size;
247   unsigned_cell address;
248   hw_memory_chunk *chunk = NULL;
249
250   /* get the alignment from the stack */
251   if (n_stack_args < stackp + 1)
252     device_error(me, "claim - incorrect number of arguments (alignment missing)");
253   alignment = stack_args[stackp];
254   stackp++;
255
256   /* get the size from the stack */
257   {
258     int i;
259     int nr_cells = device_nr_size_cells(device_parent(me));
260     if (n_stack_args < stackp + nr_cells)
261       device_error(me, "claim - incorrect number of arguments (size missing)");
262     for (i = 0; i < nr_cells - 1; i++) {
263       if (stack_args[stackp] != 0)
264         device_error(me, "claim - multi-cell sizes not supported");
265       stackp++;
266     }
267     size = stack_args[stackp];
268     stackp++;
269   }
270
271   /* get the address from the stack */
272   {
273     int nr_cells = device_nr_address_cells(device_parent(me));
274     if (alignment != 0) {
275       if (n_stack_args != stackp) {
276         if (n_stack_args == stackp + nr_cells)
277           DTRACE(memory, ("claim - extra address argument ignored\n"));
278         else
279           device_error(me, "claim - incorrect number of arguments (optional addr)");
280       }
281       address = 0;
282     }
283     else {
284       int i;
285       if (n_stack_args != stackp + nr_cells)
286         device_error(me, "claim - incorrect number of arguments (addr missing)");
287       for (i = 0; i < nr_cells - 1; i++) {
288         if (stack_args[stackp] != 0)
289           device_error(me, "claim - multi-cell addresses not supported");
290         stackp++;
291       }
292       address = stack_args[stackp];
293     }
294   }
295
296   /* check that there is space for the result */
297   if (n_stack_returns != 0
298       && n_stack_returns != device_nr_address_cells(device_parent(me)))
299     device_error(me, "claim - invalid number of return arguments");
300
301   /* find a chunk candidate, either according to address or alignment */
302   if (alignment == 0) {
303     chunk = hw_memory->heap;
304     while (chunk != NULL) {
305       if ((address + size) <= (chunk->address + chunk->size))
306         break;
307       chunk = chunk->next;
308     }
309     if (chunk == NULL || address < chunk->address || !chunk->available)
310       device_error(me, "failed to allocate %ld bytes at 0x%lx",
311                    (unsigned long)size, (unsigned long)address);
312     DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n",
313                     (unsigned long)address,
314                     (unsigned long)size));
315   }
316   else {
317     /* adjust the alignment so that it is a power of two */
318     unsigned_word align_mask = 1;
319     while (align_mask < alignment && align_mask != 0)
320       align_mask <<= 1;
321     if (align_mask == 0)
322       device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment);
323     align_mask -= 1;
324     /* now find an aligned chunk that fits */
325     chunk = hw_memory->heap;
326     while (chunk != NULL) {
327       address = ((chunk->address + align_mask) & ~align_mask);
328       if ((chunk->available)
329           && (chunk->address + chunk->size >= address + size))
330         break;
331       chunk = chunk->next;
332     }
333     if (chunk == NULL)
334       device_error(me, "failed to allocate %ld bytes with alignment %ld",
335                    (unsigned long)size, (unsigned long)alignment);
336     DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
337                     (unsigned long)size,
338                     (unsigned long)alignment,
339                     (unsigned long)alignment,
340                     (unsigned long)address));
341   }
342
343   /* break off a bit before this chunk if needed */
344   ASSERT(address >= chunk->address);
345   if (address > chunk->address) {
346     hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
347     /* insert a new chunk */
348     next_chunk->next = chunk->next;
349     chunk->next = next_chunk;
350     /* adjust the address/size */
351     next_chunk->address = address;
352     next_chunk->size = chunk->address + chunk->size - next_chunk->address;
353     next_chunk->available = 1;
354     chunk->size = next_chunk->address - chunk->address;
355     /* make this new chunk the one to allocate */
356     chunk = next_chunk;
357   }
358   ASSERT(address == chunk->address);
359
360   /* break off a bit after this chunk if needed */
361   ASSERT(address + size <= chunk->address + chunk->size);
362   if (address + size < chunk->address + chunk->size) {
363     hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
364     /* insert it in to the list */
365     next_chunk->next = chunk->next;
366     chunk->next = next_chunk;
367     /* adjust the address/size */
368     next_chunk->address = address + size;
369     next_chunk->size = chunk->address + chunk->size - next_chunk->address;
370     next_chunk->available = 1;
371     chunk->size = next_chunk->address - chunk->address;
372   }
373   ASSERT(address + size == chunk->address + chunk->size);
374
375   /* now allocate/return it */
376   chunk->available = 0;
377   hw_memory_set_available(device_instance_device(instance), hw_memory);
378   if (n_stack_returns > 0) {
379     int i;
380     for (i = 0; i < n_stack_returns - 1; i++)
381       stack_returns[i] = 0;
382     stack_returns[n_stack_returns - 1] = address;
383   }
384
385   return 0;
386 }
387
388
389 static int
390 hw_memory_instance_release(device_instance *instance,
391                            int n_stack_args,
392                            unsigned_cell stack_args[/*n_stack_args*/],
393                            int n_stack_returns,
394                            unsigned_cell stack_returns[/*n_stack_returns*/])
395 {
396   hw_memory_device *hw_memory = device_instance_data(instance);
397   device *me = device_instance_device(instance);
398   unsigned_word length;
399   unsigned_word address;
400   int stackp = 0;
401   hw_memory_chunk *chunk;
402   
403   /* get the length from the stack */
404   {
405     int i;
406     int nr_cells = device_nr_size_cells(device_parent(me));
407     if (n_stack_args < stackp + nr_cells)
408       device_error(me, "release - incorrect number of arguments (length missing)");
409     for (i = 0; i < nr_cells - 1; i++) {
410       if (stack_args[stackp] != 0)
411         device_error(me, "release - multi-cell length not supported");
412       stackp++;
413     }
414     length = stack_args[stackp];
415     stackp++;
416   }
417
418   /* get the address from the stack */
419   {
420     int i;
421     int nr_cells = device_nr_address_cells(device_parent(me));
422     if (n_stack_args != stackp + nr_cells)
423       device_error(me, "release - incorrect number of arguments (addr missing)");
424     for (i = 0; i < nr_cells - 1; i++) {
425       if (stack_args[stackp] != 0)
426         device_error(me, "release - multi-cell addresses not supported");
427       stackp++;
428     }
429     address = stack_args[stackp];
430   }
431
432   /* returns ok */
433   if (n_stack_returns != 0)
434     device_error(me, "release - nonzero number of results");
435
436   /* try to free the corresponding memory chunk */
437   chunk = hw_memory->heap;
438   while (chunk != NULL) {
439     if (chunk->address == address
440         && chunk->size == length) {
441       /* an exact match */
442       if (chunk->available)
443         device_error(me, "memory chunk 0x%lx (size 0x%lx) already available",
444                      (unsigned long)address,
445                      (unsigned long)length);
446       else {
447         /* free this chunk */
448         DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n",
449                         (unsigned long) address,
450                         (unsigned long) length));
451         chunk->available = 1;
452         break;
453       }
454     }
455     else if (chunk->address >= address
456              && chunk->address + chunk->size <= address + length) {
457       /* a sub region */
458       if (!chunk->available) {
459         DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
460                         (unsigned long) chunk->address,
461                         (unsigned long) chunk->size,
462                         (unsigned long) address,
463                         (unsigned long) length));
464         chunk->available = 1;
465       }
466     }
467     chunk = chunk->next;
468   }
469   if (chunk == NULL) {
470     printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
471                     (unsigned long)address,
472                     (unsigned long)(address + length - 1));
473   }
474
475   /* check for the chance to merge two adjacent available memory chunks */
476   chunk = hw_memory->heap;
477   while (chunk != NULL) {
478     if (chunk->available
479         && chunk->next != NULL && chunk->next->available) {
480       /* adjacent */
481       hw_memory_chunk *delete = chunk->next;
482       ASSERT(chunk->address + chunk->size == delete->address);
483       chunk->size += delete->size;
484       chunk->next = delete->next;
485       free(delete);
486     }
487     else {
488       chunk = chunk->next;
489     }
490   }
491
492   /* update the corresponding property */
493   hw_memory_set_available(device_instance_device(instance), hw_memory);
494
495   return 0;
496 }
497
498
499 static device_instance_methods hw_memory_instance_methods[] = {
500   { "claim", hw_memory_instance_claim },
501   { "release", hw_memory_instance_release },
502   { NULL, },
503 };
504
505 static device_instance_callbacks const hw_memory_instance_callbacks = {
506   hw_memory_instance_delete,
507   NULL /*read*/, NULL /*write*/, NULL /*seek*/,
508   hw_memory_instance_methods
509 };
510
511 static device_instance *
512 hw_memory_create_instance(device *me,
513                           const char *path,
514                           const char *args)
515 {
516   return device_create_instance_from(me, NULL,
517                                      device_data(me), /* nothing better */
518                                      path, args,
519                                      &hw_memory_instance_callbacks);
520 }
521
522 static device_callbacks const hw_memory_callbacks = {
523   { hw_memory_init_address, },
524   { NULL, }, /* address */
525   { NULL, }, /* IO */
526   { NULL, }, /* DMA */
527   { NULL, }, /* interrupt */
528   { NULL, }, /* unit */
529   hw_memory_create_instance,
530 };
531
532 const device_descriptor hw_memory_device_descriptor[] = {
533   { "memory", hw_memory_create, &hw_memory_callbacks },
534   { NULL },
535 };
536
537 #endif /* _HW_MEMORY_C_ */