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