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