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