gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / sim / ppc / hw_memory.c
CommitLineData
c906108c
SS
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
3fd725ef 7 the Free Software Foundation; either version 3 of the License, or
c906108c
SS
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
51b318de 16 along with this program; if not, see <http://www.gnu.org/licenses/>.
c906108c
SS
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
ceac4ecf
JS
28#include <stdlib.h>
29
c906108c
SS
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
77typedef struct _memory_reg_spec {
78 unsigned_cell base;
79 unsigned_cell size;
80} memory_reg_spec;
81
82typedef struct _hw_memory_chunk hw_memory_chunk;
83struct _hw_memory_chunk {
84 unsigned_word address;
85 unsigned_word size;
86 int available;
87 hw_memory_chunk *next;
88};
89
90typedef struct _hw_memory_device {
91 hw_memory_chunk *heap;
92} hw_memory_device;
93
94
95static void *
96hw_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
105static void
106hw_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);
d79fe0d6 142 free(available);
c906108c
SS
143}
144
145
146static void
147hw_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;
d79fe0d6 159 free(dead_chunk);
c906108c
SS
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
231static void
232hw_memory_instance_delete(device_instance *instance)
233{
234 return;
235}
236
237static int
238hw_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
391static int
392hw_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;
d79fe0d6 487 free(delete);
c906108c
SS
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
501static device_instance_methods hw_memory_instance_methods[] = {
502 { "claim", hw_memory_instance_claim },
503 { "release", hw_memory_instance_release },
504 { NULL, },
505};
506
507static 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
513static device_instance *
514hw_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
524static 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
534const device_descriptor hw_memory_device_descriptor[] = {
535 { "memory", hw_memory_create, &hw_memory_callbacks },
536 { NULL },
537};
538
539#endif /* _HW_MEMORY_C_ */
This page took 0.948728 seconds and 4 git commands to generate.