]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/ppc/hw_memory.c
Initial creation of sourceware repository
[thirdparty/binutils-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 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 zfree(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 zfree(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 zfree(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_ */