aboutsummaryrefslogtreecommitdiff
path: root/sim/ppc/hw_memory.c
diff options
context:
space:
mode:
authorMichael Meissner <gnu@the-meissners.org>1996-07-23 15:42:42 +0000
committerMichael Meissner <gnu@the-meissners.org>1996-07-23 15:42:42 +0000
commit30c87b55ec44de3a56cecf41fc7c93ed0c4005ca (patch)
tree5752ad56a8e4c990eb02b12f68d486415c79c41b /sim/ppc/hw_memory.c
parent856bcbcbbc01a252a8df2221038100e9a33f87ff (diff)
downloadfsf-binutils-gdb-30c87b55ec44de3a56cecf41fc7c93ed0c4005ca.zip
fsf-binutils-gdb-30c87b55ec44de3a56cecf41fc7c93ed0c4005ca.tar.gz
fsf-binutils-gdb-30c87b55ec44de3a56cecf41fc7c93ed0c4005ca.tar.bz2
New simulator changes from Andrew
Diffstat (limited to 'sim/ppc/hw_memory.c')
-rw-r--r--sim/ppc/hw_memory.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/sim/ppc/hw_memory.c b/sim/ppc/hw_memory.c
new file mode 100644
index 0000000..7e4204d
--- /dev/null
+++ b/sim/ppc/hw_memory.c
@@ -0,0 +1,316 @@
+/* This file is part of the program psim.
+
+ Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#ifndef _HW_MEMORY_C_
+#define _HW_MEMORY_C_
+
+#ifndef STATIC_INLINE_HW_MEMORY
+#define STATIC_INLINE_HW_MEMORY STATIC_INLINE
+#endif
+
+#include "device_table.h"
+
+/* DEVICE
+
+ memory - description of system memory
+
+ DESCRIPTION
+
+ This device describes the size and location of the banks of
+ physical memory within the simulation.
+
+ In addition, this device supports the "claim" and "release" methods
+ that can be used by OpenBoot client programs to manage the
+ allocation of physical memory.
+
+ PROPERTIES
+
+ reg = { <address> <size> } (required)
+
+ Each pair specify one bank of memory.
+
+ available = { <address> <size> } (automatic)
+
+ Each pair specifies a block of memory that is currently unallocated.
+
+ */
+
+typedef struct _memory_reg_spec {
+ unsigned32 base;
+ unsigned32 size;
+} memory_reg_spec;
+
+typedef struct _hw_memory_chunk hw_memory_chunk;
+struct _hw_memory_chunk {
+ unsigned_word address;
+ unsigned_word size;
+ unsigned_word alloc_address;
+ unsigned_word alloc_size;
+ int available;
+ hw_memory_chunk *next;
+};
+
+typedef struct _hw_memory_device {
+ hw_memory_chunk *heap;
+} hw_memory_device;
+
+
+static void *
+hw_memory_create(const char *name,
+ const device_unit *unit_address,
+ const char *args)
+{
+ hw_memory_device *hw_memory = ZALLOC(hw_memory_device);
+ return hw_memory;
+}
+
+
+static void
+hw_memory_set_available(device *me,
+ hw_memory_device *hw_memory)
+{
+ hw_memory_chunk *chunk = NULL;
+ memory_reg_spec *available = NULL;
+ int nr_available = 0;
+ int curr = 0;
+ int sizeof_available = 0;
+ /* determine the nr of available chunks */
+ chunk = hw_memory->heap;
+ nr_available = 0;
+ while (chunk != NULL) {
+ if (chunk->available)
+ nr_available += 1;
+ chunk = chunk->next;
+ }
+ /* now create the available struct */
+ ASSERT(nr_available > 0);
+ sizeof_available = sizeof(memory_reg_spec) * nr_available;
+ available = zalloc(sizeof_available);
+ chunk = hw_memory->heap;
+ curr = 0;
+ while (chunk != NULL) {
+ if (chunk->available) {
+ available[curr].base = H2BE_4(chunk->address);
+ available[curr].size = H2BE_4(chunk->size);
+ curr += 1;
+ }
+ chunk = chunk->next;
+ }
+ /* update */
+ device_set_array_property(me, "available", available, sizeof_available);
+ zfree(available);
+}
+
+
+static void
+hw_memory_init_address(device *me)
+{
+ hw_memory_device *hw_memory = (hw_memory_device*)device_data(me);
+ const device_property *reg = device_find_array_property(me, "reg");
+ const memory_reg_spec *spec = reg->array;
+ int nr_entries = reg->sizeof_array / sizeof(*spec);
+
+ /* sanity check reg property */
+ if ((reg->sizeof_array % sizeof(*spec)) != 0)
+ error("devices/%s reg property of incorrect size\n", device_name(me));
+
+ /* free up any previous structures */
+ {
+ hw_memory_chunk *curr_chunk = hw_memory->heap;
+ hw_memory->heap = NULL;
+ while (curr_chunk != NULL) {
+ hw_memory_chunk *dead_chunk = curr_chunk;
+ curr_chunk = dead_chunk->next;
+ dead_chunk->next = NULL;
+ zfree(dead_chunk);
+ }
+ }
+
+ /* count/allocate memory entries */
+ {
+ hw_memory_chunk **curr_chunk = &hw_memory->heap;
+ while (nr_entries > 0) {
+ hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk);
+ new_chunk->address = BE2H_4(spec->base);
+ new_chunk->size = BE2H_4(spec->size);
+ new_chunk->available = 1;
+ device_attach_address(device_parent(me),
+ device_name(me),
+ attach_raw_memory,
+ 0 /*address space*/,
+ new_chunk->address,
+ new_chunk->size,
+ access_read_write_exec,
+ me);
+ spec++;
+ nr_entries--;
+ *curr_chunk = new_chunk;
+ curr_chunk = &new_chunk->next;
+ }
+ }
+
+ /* initialize the alloc property for this device */
+ hw_memory_set_available(me, hw_memory);
+}
+
+static void
+hw_memory_instance_delete(device_instance *instance)
+{
+ return;
+}
+
+static unsigned_word
+hw_memory_instance_claim(device_instance *instance,
+ unsigned_word address,
+ unsigned_word size,
+ unsigned_word alignment)
+{
+ hw_memory_device *hw_memory = device_instance_data(instance);
+ hw_memory_chunk *chunk = NULL;
+ DTRACE(memory, ("claim - address=0x%lx size=0x%lx alignment=%d\n",
+ (unsigned long)address,
+ (unsigned long)size,
+ (int)alignment));
+ /* find a chunk candidate, either according to address or alignment */
+ if (alignment == 0) {
+ chunk = hw_memory->heap;
+ while (chunk != NULL
+ && (address+size) > (chunk->address+chunk->size))
+ chunk = chunk->next;
+ if (chunk == NULL || address < chunk->address || !chunk->available)
+ error("hw_memory_instance_claim: failed to allocate @0x%lx, size %ld\n",
+ (unsigned long)address, (unsigned long)size);
+ }
+ else {
+ chunk = hw_memory->heap;
+ while (chunk != NULL && chunk->size < size)
+ chunk = chunk->next;
+ if (chunk == NULL || FLOOR_PAGE(alignment-1) > 0)
+ error("hw_memory_instance_claim: failed to allocate %ld, align %ld\n",
+ (unsigned long)size, (unsigned long)size);
+ address = chunk->address;
+ }
+ /* break of a part before this memory if needed */
+ ASSERT(address >= chunk->address);
+ if (FLOOR_PAGE(address) > chunk->address) {
+ hw_memory_chunk *last_chunk = chunk;
+ /* insert a new earlier chunk */
+ chunk = ZALLOC(hw_memory_chunk);
+ chunk->next = last_chunk->next;
+ last_chunk->next = chunk;
+ /* adjust the address/size */
+ chunk->address = FLOOR_PAGE(address);
+ chunk->size = last_chunk->size - (chunk->address - last_chunk->address);
+ last_chunk->size = chunk->address - last_chunk->address;
+ }
+ ASSERT(FLOOR_PAGE(address) == chunk->address);
+ /* break of a bit after this chunk if needed */
+ if (ALIGN_PAGE(address+size) < chunk->address + chunk->size) {
+ hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
+ /* insert it in to the list */
+ next_chunk->next = chunk->next;
+ chunk->next = next_chunk;
+ next_chunk->available = 1;
+ /* adjust the address/size */
+ next_chunk->address = ALIGN_PAGE(address+size);
+ next_chunk->size = chunk->address + chunk->size - next_chunk->address;
+ chunk->size = next_chunk->address - chunk->address;
+ }
+ ASSERT(ALIGN_PAGE(address+size) == chunk->address + chunk->size);
+ /* now allocate it */
+ chunk->alloc_address = address;
+ chunk->alloc_size = size;
+ chunk->available = 0;
+ hw_memory_set_available(device_instance_device(instance), hw_memory);
+ return address;
+}
+
+static void
+hw_memory_instance_release(device_instance *instance,
+ unsigned_word address,
+ unsigned_word length)
+{
+ hw_memory_device *hw_memory = device_instance_data(instance);
+ hw_memory_chunk *chunk = hw_memory->heap;
+ while (chunk != NULL) {
+ if (chunk->alloc_address == address
+ && chunk->alloc_size == length
+ && chunk->available == 0) {
+ /* free this chunk */
+ chunk->available = 1;
+ /* check for merge */
+ chunk = hw_memory->heap;
+ while (chunk != NULL) {
+ if (chunk->available
+ && chunk->next != NULL && chunk->next->available) {
+ /* adjacent */
+ hw_memory_chunk *delete = chunk->next;
+ ASSERT(chunk->address + chunk->size == delete->address);
+ chunk->size += delete->size;
+ chunk->next = delete->next;
+ zfree(delete);
+ }
+ }
+ /* update the corresponding property */
+ hw_memory_set_available(device_instance_device(instance), hw_memory);
+ return;
+ }
+ chunk = chunk->next;
+ }
+ error("hw_memory_instance_release: Address 0x%lx, size %ld not found\n",
+ (unsigned long)address, (unsigned long)length);
+ /* FIXME - dump allocated */
+ /* FIXME - dump arguments */
+}
+
+static device_instance_callbacks const hw_memory_instance_callbacks = {
+ hw_memory_instance_delete,
+ NULL /*read*/, NULL /*write*/, NULL /*seek*/,
+ hw_memory_instance_claim, hw_memory_instance_release
+};
+
+static device_instance *
+hw_memory_create_instance(device *me,
+ const char *path,
+ const char *args)
+{
+ return device_create_instance_from(me, NULL,
+ device_data(me), /* nothing better */
+ path, args,
+ &hw_memory_instance_callbacks);
+}
+
+static device_callbacks const hw_memory_callbacks = {
+ { hw_memory_init_address, },
+ { NULL, }, /* address */
+ { NULL, }, /* IO */
+ { NULL, }, /* DMA */
+ { NULL, }, /* interrupt */
+ { NULL, }, /* unit */
+ hw_memory_create_instance,
+};
+
+const device_descriptor hw_memory_device_descriptor[] = {
+ { "memory", hw_memory_create, &hw_memory_callbacks },
+ { NULL },
+};
+
+#endif /* _HW_MEMORY_C_ */