123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /* This file is part of the program psim.
- Copyright (C) 1994-1997, 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 3 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, see <http://www.gnu.org/licenses/>.
-
- */
- #ifndef _HW_MEMORY_C_
- #define _HW_MEMORY_C_
- #ifndef STATIC_INLINE_HW_MEMORY
- #define STATIC_INLINE_HW_MEMORY STATIC_INLINE
- #endif
- #include <stdlib.h>
- #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.
- BUGS
- OpenFirmware doesn't make it clear if, when releasing memory the
- same address + size pair as was used during the claim should be
- specified.
- It is assumed that #size-cells and #address-cells for the parent
- node of this device are both one i.e. an address or size can be
- specified using a single memory cell (word).
- Significant work will be required before the <<memory>> device can
- support 64bit addresses (#address-cells equal two).
- */
- typedef struct _memory_reg_spec {
- unsigned_cell base;
- unsigned_cell size;
- } memory_reg_spec;
- typedef struct _hw_memory_chunk hw_memory_chunk;
- struct _hw_memory_chunk {
- unsigned_word address;
- unsigned_word 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;
- ASSERT(chunk->next == NULL
- || chunk->address < chunk->next->address);
- ASSERT(chunk->next == NULL
- || chunk->address + chunk->size == chunk->next->address);
- 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_cell(chunk->address);
- available[curr].size = H2BE_cell(chunk->size);
- curr += 1;
- }
- chunk = chunk->next;
- }
- /* update */
- device_set_array_property(me, "available", available, sizeof_available);
- free(available);
- }
- static void
- hw_memory_init_address(device *me)
- {
- hw_memory_device *hw_memory = (hw_memory_device*)device_data(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;
- free(dead_chunk);
- }
- }
- /* attach memory regions according to the "reg" property */
- {
- int reg_nr;
- reg_property_spec reg;
- for (reg_nr = 0;
- device_find_reg_array_property(me, "reg", reg_nr, ®);
- reg_nr++) {
- int i;
- /* check that the entry meets restrictions */
- for (i = 0; i < reg.address.nr_cells - 1; i++)
- if (reg.address.cells[i] != 0)
- device_error(me, "Only single celled addresses supported");
- for (i = 0; i < reg.size.nr_cells - 1; i++)
- if (reg.size.cells[i] != 0)
- device_error(me, "Only single celled sizes supported");
- /* attach the range */
- device_attach_address(device_parent(me),
- attach_raw_memory,
- 0 /*address space*/,
- reg.address.cells[reg.address.nr_cells - 1],
- reg.size.cells[reg.size.nr_cells - 1],
- access_read_write_exec,
- me);
- }
- }
- /* create the initial `available memory' data structure */
- if (device_find_property(me, "available") != NULL) {
- hw_memory_chunk **curr_chunk = &hw_memory->heap;
- int cell_nr;
- unsigned_cell dummy;
- int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy);
- if ((nr_cells % 2) != 0)
- device_error(me, "property \"available\" invalid - contains an odd number of cells");
- for (cell_nr = 0;
- cell_nr < nr_cells;
- cell_nr += 2) {
- hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk);
- device_find_integer_array_property(me, "available", cell_nr,
- &new_chunk->address);
- device_find_integer_array_property(me, "available", cell_nr + 1,
- &new_chunk->size);
- new_chunk->available = 1;
- *curr_chunk = new_chunk;
- curr_chunk = &new_chunk->next;
- }
- }
- else {
- hw_memory_chunk **curr_chunk = &hw_memory->heap;
- int reg_nr;
- reg_property_spec reg;
- for (reg_nr = 0;
- device_find_reg_array_property(me, "reg", reg_nr, ®);
- reg_nr++) {
- hw_memory_chunk *new_chunk;
- new_chunk = ZALLOC(hw_memory_chunk);
- new_chunk->address = reg.address.cells[reg.address.nr_cells - 1];
- new_chunk->size = reg.size.cells[reg.size.nr_cells - 1];
- new_chunk->available = 1;
- *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 int
- hw_memory_instance_claim(device_instance *instance,
- int n_stack_args,
- unsigned_cell stack_args[/*n_stack_args*/],
- int n_stack_returns,
- unsigned_cell stack_returns[/*n_stack_returns*/])
- {
- hw_memory_device *hw_memory = device_instance_data(instance);
- device *me = device_instance_device(instance);
- int stackp = 0;
- unsigned_word alignment;
- unsigned_cell size;
- unsigned_cell address;
- hw_memory_chunk *chunk = NULL;
- /* get the alignment from the stack */
- if (n_stack_args < stackp + 1)
- device_error(me, "claim - incorrect number of arguments (alignment missing)");
- alignment = stack_args[stackp];
- stackp++;
- /* get the size from the stack */
- {
- int i;
- int nr_cells = device_nr_size_cells(device_parent(me));
- if (n_stack_args < stackp + nr_cells)
- device_error(me, "claim - incorrect number of arguments (size missing)");
- for (i = 0; i < nr_cells - 1; i++) {
- if (stack_args[stackp] != 0)
- device_error(me, "claim - multi-cell sizes not supported");
- stackp++;
- }
- size = stack_args[stackp];
- stackp++;
- }
- /* get the address from the stack */
- {
- int nr_cells = device_nr_address_cells(device_parent(me));
- if (alignment != 0) {
- if (n_stack_args != stackp) {
- if (n_stack_args == stackp + nr_cells)
- DTRACE(memory, ("claim - extra address argument ignored\n"));
- else
- device_error(me, "claim - incorrect number of arguments (optional addr)");
- }
- address = 0;
- }
- else {
- int i;
- if (n_stack_args != stackp + nr_cells)
- device_error(me, "claim - incorrect number of arguments (addr missing)");
- for (i = 0; i < nr_cells - 1; i++) {
- if (stack_args[stackp] != 0)
- device_error(me, "claim - multi-cell addresses not supported");
- stackp++;
- }
- address = stack_args[stackp];
- }
- }
- /* check that there is space for the result */
- if (n_stack_returns != 0
- && n_stack_returns != device_nr_address_cells(device_parent(me)))
- device_error(me, "claim - invalid number of return arguments");
- /* find a chunk candidate, either according to address or alignment */
- if (alignment == 0) {
- chunk = hw_memory->heap;
- while (chunk != NULL) {
- if ((address + size) <= (chunk->address + chunk->size))
- break;
- chunk = chunk->next;
- }
- if (chunk == NULL || address < chunk->address || !chunk->available)
- device_error(me, "failed to allocate %ld bytes at 0x%lx",
- (unsigned long)size, (unsigned long)address);
- DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n",
- (unsigned long)address,
- (unsigned long)size));
- }
- else {
- /* adjust the alignment so that it is a power of two */
- unsigned_word align_mask = 1;
- while (align_mask < alignment && align_mask != 0)
- align_mask <<= 1;
- if (align_mask == 0)
- device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment);
- align_mask -= 1;
- /* now find an aligned chunk that fits */
- chunk = hw_memory->heap;
- while (chunk != NULL) {
- address = ((chunk->address + align_mask) & ~align_mask);
- if ((chunk->available)
- && (chunk->address + chunk->size >= address + size))
- break;
- chunk = chunk->next;
- }
- if (chunk == NULL)
- device_error(me, "failed to allocate %ld bytes with alignment %ld",
- (unsigned long)size, (unsigned long)alignment);
- DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n",
- (unsigned long)size,
- (unsigned long)alignment,
- (unsigned long)alignment,
- (unsigned long)address));
- }
- /* break off a bit before this chunk if needed */
- ASSERT(address >= chunk->address);
- if (address > chunk->address) {
- hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk);
- /* insert a new chunk */
- next_chunk->next = chunk->next;
- chunk->next = next_chunk;
- /* adjust the address/size */
- next_chunk->address = address;
- next_chunk->size = chunk->address + chunk->size - next_chunk->address;
- next_chunk->available = 1;
- chunk->size = next_chunk->address - chunk->address;
- /* make this new chunk the one to allocate */
- chunk = next_chunk;
- }
- ASSERT(address == chunk->address);
- /* break off a bit after this chunk if needed */
- ASSERT(address + size <= chunk->address + chunk->size);
- if (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;
- /* adjust the address/size */
- next_chunk->address = address + size;
- next_chunk->size = chunk->address + chunk->size - next_chunk->address;
- next_chunk->available = 1;
- chunk->size = next_chunk->address - chunk->address;
- }
- ASSERT(address + size == chunk->address + chunk->size);
- /* now allocate/return it */
- chunk->available = 0;
- hw_memory_set_available(device_instance_device(instance), hw_memory);
- if (n_stack_returns > 0) {
- int i;
- for (i = 0; i < n_stack_returns - 1; i++)
- stack_returns[i] = 0;
- stack_returns[n_stack_returns - 1] = address;
- }
- return 0;
- }
- static int
- hw_memory_instance_release(device_instance *instance,
- int n_stack_args,
- unsigned_cell stack_args[/*n_stack_args*/],
- int n_stack_returns,
- unsigned_cell stack_returns[/*n_stack_returns*/])
- {
- hw_memory_device *hw_memory = device_instance_data(instance);
- device *me = device_instance_device(instance);
- unsigned_word length;
- unsigned_word address;
- int stackp = 0;
- hw_memory_chunk *chunk;
-
- /* get the length from the stack */
- {
- int i;
- int nr_cells = device_nr_size_cells(device_parent(me));
- if (n_stack_args < stackp + nr_cells)
- device_error(me, "release - incorrect number of arguments (length missing)");
- for (i = 0; i < nr_cells - 1; i++) {
- if (stack_args[stackp] != 0)
- device_error(me, "release - multi-cell length not supported");
- stackp++;
- }
- length = stack_args[stackp];
- stackp++;
- }
- /* get the address from the stack */
- {
- int i;
- int nr_cells = device_nr_address_cells(device_parent(me));
- if (n_stack_args != stackp + nr_cells)
- device_error(me, "release - incorrect number of arguments (addr missing)");
- for (i = 0; i < nr_cells - 1; i++) {
- if (stack_args[stackp] != 0)
- device_error(me, "release - multi-cell addresses not supported");
- stackp++;
- }
- address = stack_args[stackp];
- }
- /* returns ok */
- if (n_stack_returns != 0)
- device_error(me, "release - nonzero number of results");
- /* try to free the corresponding memory chunk */
- chunk = hw_memory->heap;
- while (chunk != NULL) {
- if (chunk->address == address
- && chunk->size == length) {
- /* an exact match */
- if (chunk->available)
- device_error(me, "memory chunk 0x%lx (size 0x%lx) already available",
- (unsigned long)address,
- (unsigned long)length);
- else {
- /* free this chunk */
- DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n",
- (unsigned long) address,
- (unsigned long) length));
- chunk->available = 1;
- break;
- }
- }
- else if (chunk->address >= address
- && chunk->address + chunk->size <= address + length) {
- /* a sub region */
- if (!chunk->available) {
- DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n",
- (unsigned long) chunk->address,
- (unsigned long) chunk->size,
- (unsigned long) address,
- (unsigned long) length));
- chunk->available = 1;
- }
- }
- chunk = chunk->next;
- }
- if (chunk == NULL) {
- printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n",
- (unsigned long)address,
- (unsigned long)(address + length - 1));
- }
- /* check for the chance to merge two adjacent available memory chunks */
- 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;
- free(delete);
- }
- else {
- chunk = chunk->next;
- }
- }
- /* update the corresponding property */
- hw_memory_set_available(device_instance_device(instance), hw_memory);
- return 0;
- }
- static device_instance_methods hw_memory_instance_methods[] = {
- { "claim", hw_memory_instance_claim },
- { "release", hw_memory_instance_release },
- { NULL, },
- };
- static device_instance_callbacks const hw_memory_instance_callbacks = {
- hw_memory_instance_delete,
- NULL /*read*/, NULL /*write*/, NULL /*seek*/,
- hw_memory_instance_methods
- };
- 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_ */
|