|  | /* Simulator memory option handling. | 
|  | Copyright (C) 1996-2024 Free Software Foundation, Inc. | 
|  | Contributed by Cygnus Support. | 
|  |  | 
|  | This file is part of GDB, the GNU debugger. | 
|  |  | 
|  | 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/>.  */ | 
|  |  | 
|  | /* This must come before any other includes.  */ | 
|  | #include "defs.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #ifdef HAVE_FCNTL_H | 
|  | #include <fcntl.h> | 
|  | #endif | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #ifdef HAVE_SYS_MMAN_H | 
|  | #include <sys/mman.h> | 
|  | #endif | 
|  | #include <sys/stat.h> | 
|  |  | 
|  | #include "sim-main.h" | 
|  | #include "sim-assert.h" | 
|  | #include "sim-options.h" | 
|  |  | 
|  | /* Memory fill byte. */ | 
|  | static uint8_t fill_byte_value; | 
|  | static int fill_byte_flag = 0; | 
|  |  | 
|  | /* Memory mapping; see OPTION_MEMORY_MAPFILE. */ | 
|  | static int mmap_next_fd = -1; | 
|  |  | 
|  | /* Memory command line options. */ | 
|  |  | 
|  | enum { | 
|  | OPTION_MEMORY_DELETE = OPTION_START, | 
|  | OPTION_MEMORY_REGION, | 
|  | OPTION_MEMORY_SIZE, | 
|  | OPTION_MEMORY_INFO, | 
|  | OPTION_MEMORY_ALIAS, | 
|  | OPTION_MEMORY_CLEAR, | 
|  | OPTION_MEMORY_FILL, | 
|  | OPTION_MEMORY_MAPFILE, | 
|  | OPTION_MAP_INFO | 
|  | }; | 
|  |  | 
|  | static DECLARE_OPTION_HANDLER (memory_option_handler); | 
|  |  | 
|  | static const OPTION memory_options[] = | 
|  | { | 
|  | { {"memory-delete", required_argument, NULL, OPTION_MEMORY_DELETE }, | 
|  | '\0', "ADDRESS|all", "Delete memory at ADDRESS (all addresses)", | 
|  | memory_option_handler }, | 
|  | { {"delete-memory", required_argument, NULL, OPTION_MEMORY_DELETE }, | 
|  | '\0', "ADDRESS", NULL, | 
|  | memory_option_handler }, | 
|  |  | 
|  | { {"memory-region", required_argument, NULL, OPTION_MEMORY_REGION }, | 
|  | '\0', "ADDRESS,SIZE[,MODULO]", "Add a memory region", | 
|  | memory_option_handler }, | 
|  |  | 
|  | { {"memory-alias", required_argument, NULL, OPTION_MEMORY_ALIAS }, | 
|  | '\0', "ADDRESS,SIZE{,ADDRESS}", "Add memory shadow", | 
|  | memory_option_handler }, | 
|  |  | 
|  | { {"memory-size", required_argument, NULL, OPTION_MEMORY_SIZE }, | 
|  | '\0', "<size>[in bytes, Kb (k suffix), Mb (m suffix) or Gb (g suffix)]", | 
|  | "Add memory at address zero", memory_option_handler }, | 
|  |  | 
|  | { {"memory-fill", required_argument, NULL, OPTION_MEMORY_FILL }, | 
|  | '\0', "VALUE", "Fill subsequently added memory regions", | 
|  | memory_option_handler }, | 
|  |  | 
|  | { {"memory-clear", no_argument, NULL, OPTION_MEMORY_CLEAR }, | 
|  | '\0', NULL, "Clear subsequently added memory regions", | 
|  | memory_option_handler }, | 
|  |  | 
|  | #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) | 
|  | { {"memory-mapfile", required_argument, NULL, OPTION_MEMORY_MAPFILE }, | 
|  | '\0', "FILE", "Memory-map next memory region from file", | 
|  | memory_option_handler }, | 
|  | #endif | 
|  |  | 
|  | { {"memory-info", no_argument, NULL, OPTION_MEMORY_INFO }, | 
|  | '\0', NULL, "List configurable memory regions", | 
|  | memory_option_handler }, | 
|  | { {"info-memory", no_argument, NULL, OPTION_MEMORY_INFO }, | 
|  | '\0', NULL, NULL, | 
|  | memory_option_handler }, | 
|  | { {"map-info", no_argument, NULL, OPTION_MAP_INFO }, | 
|  | '\0', NULL, "List mapped regions", | 
|  | memory_option_handler }, | 
|  |  | 
|  | { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } | 
|  | }; | 
|  |  | 
|  |  | 
|  | static sim_memopt * | 
|  | do_memopt_add (SIM_DESC sd, | 
|  | int level, | 
|  | int space, | 
|  | address_word addr, | 
|  | address_word nr_bytes, | 
|  | unsigned modulo, | 
|  | sim_memopt **entry, | 
|  | void *buffer) | 
|  | { | 
|  | void *fill_buffer; | 
|  | unsigned fill_length; | 
|  | void *free_buffer; | 
|  | unsigned long free_length; | 
|  |  | 
|  | if (buffer != NULL) | 
|  | { | 
|  | /* Buffer already given.  sim_memory_uninstall will free it. */ | 
|  | sim_core_attach (sd, NULL, | 
|  | level, access_read_write_exec, space, | 
|  | addr, nr_bytes, modulo, NULL, buffer); | 
|  |  | 
|  | free_buffer = buffer; | 
|  | free_length = 0; | 
|  | fill_buffer = buffer; | 
|  | fill_length = (modulo == 0) ? nr_bytes : modulo; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Allocate new well-aligned buffer, just as sim_core_attach(). */ | 
|  | void *aligned_buffer; | 
|  | int padding = (addr % sizeof (uint64_t)); | 
|  | unsigned long bytes; | 
|  |  | 
|  | #ifdef HAVE_MMAP | 
|  | struct stat s; | 
|  |  | 
|  | if (mmap_next_fd >= 0) | 
|  | { | 
|  | /* Check that given file is big enough. */ | 
|  | int rc = fstat (mmap_next_fd, &s); | 
|  |  | 
|  | if (rc < 0) | 
|  | sim_io_error (sd, "Error, unable to stat file: %s\n", | 
|  | strerror (errno)); | 
|  |  | 
|  | /* Autosize the mapping to the file length.  */ | 
|  | if (nr_bytes == 0) | 
|  | nr_bytes = s.st_size; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bytes = (modulo == 0 ? nr_bytes : modulo) + padding; | 
|  |  | 
|  | free_buffer = NULL; | 
|  | free_length = bytes; | 
|  |  | 
|  | #ifdef HAVE_MMAP | 
|  | /* Memory map or malloc(). */ | 
|  | if (mmap_next_fd >= 0) | 
|  | { | 
|  | /* Some kernels will SIGBUS the application if mmap'd file | 
|  | is not large enough.  */ | 
|  | if (s.st_size < bytes) | 
|  | { | 
|  | sim_io_error (sd, | 
|  | "Error, cannot confirm that mmap file is large enough " | 
|  | "(>= %ld bytes)\n", bytes); | 
|  | } | 
|  |  | 
|  | free_buffer = mmap (0, bytes, PROT_READ|PROT_WRITE, MAP_SHARED, mmap_next_fd, 0); | 
|  | if (free_buffer == 0 || free_buffer == (char*)-1) /* MAP_FAILED */ | 
|  | { | 
|  | sim_io_error (sd, "Error, cannot mmap file (%s).\n", | 
|  | strerror (errno)); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Need heap allocation? */ | 
|  | if (free_buffer == NULL) | 
|  | { | 
|  | /* If filling with non-zero value, do not use clearing allocator. */ | 
|  | if (fill_byte_flag && fill_byte_value != 0) | 
|  | free_buffer = xmalloc (bytes); /* don't clear */ | 
|  | else | 
|  | free_buffer = zalloc (bytes); /* clear */ | 
|  | } | 
|  |  | 
|  | aligned_buffer = (char*) free_buffer + padding; | 
|  |  | 
|  | sim_core_attach (sd, NULL, | 
|  | level, access_read_write_exec, space, | 
|  | addr, nr_bytes, modulo, NULL, aligned_buffer); | 
|  |  | 
|  | fill_buffer = aligned_buffer; | 
|  | fill_length = (modulo == 0) ? nr_bytes : modulo; | 
|  |  | 
|  | /* If we just used a clearing allocator, and are about to fill with | 
|  | zero, truncate the redundant fill operation. */ | 
|  |  | 
|  | if (fill_byte_flag && fill_byte_value == 0) | 
|  | fill_length = 1; /* avoid boundary length=0 case */ | 
|  | } | 
|  |  | 
|  | if (fill_byte_flag) | 
|  | { | 
|  | ASSERT (fill_buffer != 0); | 
|  | memset ((char*) fill_buffer, fill_byte_value, fill_length); | 
|  | } | 
|  |  | 
|  | while ((*entry) != NULL) | 
|  | entry = &(*entry)->next; | 
|  | (*entry) = ZALLOC (sim_memopt); | 
|  | (*entry)->level = level; | 
|  | (*entry)->space = space; | 
|  | (*entry)->addr = addr; | 
|  | (*entry)->nr_bytes = nr_bytes; | 
|  | (*entry)->modulo = modulo; | 
|  | (*entry)->buffer = free_buffer; | 
|  |  | 
|  | /* Record memory unmapping info.  */ | 
|  | if (mmap_next_fd >= 0) | 
|  | { | 
|  | (*entry)->munmap_length = free_length; | 
|  | close (mmap_next_fd); | 
|  | mmap_next_fd = -1; | 
|  | } | 
|  | else | 
|  | (*entry)->munmap_length = 0; | 
|  |  | 
|  | return (*entry); | 
|  | } | 
|  |  | 
|  | static SIM_RC | 
|  | do_memopt_delete (SIM_DESC sd, | 
|  | int level, | 
|  | int space, | 
|  | address_word addr) | 
|  | { | 
|  | sim_memopt **entry = &STATE_MEMOPT (sd); | 
|  | sim_memopt *alias; | 
|  | while ((*entry) != NULL | 
|  | && ((*entry)->level != level | 
|  | || (*entry)->space != space | 
|  | || (*entry)->addr != addr)) | 
|  | entry = &(*entry)->next; | 
|  | if ((*entry) == NULL) | 
|  | { | 
|  | sim_io_eprintf (sd, "Memory at 0x%lx not found, not deleted\n", | 
|  | (long) addr); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  | /* delete any buffer */ | 
|  | if ((*entry)->buffer != NULL) | 
|  | { | 
|  | #ifdef HAVE_MUNMAP | 
|  | if ((*entry)->munmap_length > 0) | 
|  | munmap ((*entry)->buffer, (*entry)->munmap_length); | 
|  | else | 
|  | #endif | 
|  | free ((*entry)->buffer); | 
|  | } | 
|  |  | 
|  | /* delete it and its aliases */ | 
|  | alias = *entry; | 
|  | *entry = (*entry)->next; | 
|  | while (alias != NULL) | 
|  | { | 
|  | sim_memopt *dead = alias; | 
|  | alias = alias->alias; | 
|  | sim_core_detach (sd, NULL, dead->level, dead->space, dead->addr); | 
|  | free (dead); | 
|  | } | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static char * | 
|  | parse_size (char *chp, | 
|  | address_word *nr_bytes, | 
|  | unsigned *modulo) | 
|  | { | 
|  | /* <nr_bytes>[K|M|G] [ "%" <modulo> ] */ | 
|  | *nr_bytes = strtoul (chp, &chp, 0); | 
|  | switch (*chp) | 
|  | { | 
|  | case '%': | 
|  | *modulo = strtoul (chp + 1, &chp, 0); | 
|  | break; | 
|  | case 'g': case 'G': /* Gigabyte suffix.  */ | 
|  | *nr_bytes <<= 10; | 
|  | ATTRIBUTE_FALLTHROUGH; | 
|  | case 'm': case 'M': /* Megabyte suffix.  */ | 
|  | *nr_bytes <<= 10; | 
|  | ATTRIBUTE_FALLTHROUGH; | 
|  | case 'k': case 'K': /* Kilobyte suffix.  */ | 
|  | *nr_bytes <<= 10; | 
|  | /* Check for a modulo specifier after the suffix.  */ | 
|  | ++ chp; | 
|  | if (* chp == 'b' || * chp == 'B') | 
|  | ++ chp; | 
|  | if (* chp == '%') | 
|  | *modulo = strtoul (chp + 1, &chp, 0); | 
|  | break; | 
|  | } | 
|  | return chp; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | parse_ulong_value (char *chp, | 
|  | unsigned long *value) | 
|  | { | 
|  | *value = strtoul (chp, &chp, 0); | 
|  | return chp; | 
|  | } | 
|  |  | 
|  | static char * | 
|  | parse_addr (char *chp, | 
|  | int *level, | 
|  | int *space, | 
|  | address_word *addr) | 
|  | { | 
|  | /* [ <space> ": " ] <addr> [ "@" <level> ] */ | 
|  | *addr = (unsigned long) strtoul (chp, &chp, 0); | 
|  | if (*chp == ':') | 
|  | { | 
|  | *space = *addr; | 
|  | *addr = (unsigned long) strtoul (chp + 1, &chp, 0); | 
|  | } | 
|  | if (*chp == '@') | 
|  | { | 
|  | *level = strtoul (chp + 1, &chp, 0); | 
|  | } | 
|  | return chp; | 
|  | } | 
|  |  | 
|  |  | 
|  | static SIM_RC | 
|  | memory_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt, | 
|  | char *arg, int is_command) | 
|  | { | 
|  | switch (opt) | 
|  | { | 
|  |  | 
|  | case OPTION_MEMORY_DELETE: | 
|  | if (strcasecmp (arg, "all") == 0) | 
|  | { | 
|  | while (STATE_MEMOPT (sd) != NULL) | 
|  | do_memopt_delete (sd, | 
|  | STATE_MEMOPT (sd)->level, | 
|  | STATE_MEMOPT (sd)->space, | 
|  | STATE_MEMOPT (sd)->addr); | 
|  | return SIM_RC_OK; | 
|  | } | 
|  | else | 
|  | { | 
|  | int level = 0; | 
|  | int space = 0; | 
|  | address_word addr = 0; | 
|  | parse_addr (arg, &level, &space, &addr); | 
|  | return do_memopt_delete (sd, level, space, addr); | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_REGION: | 
|  | { | 
|  | char *chp = arg; | 
|  | int level = 0; | 
|  | int space = 0; | 
|  | address_word addr = 0; | 
|  | address_word nr_bytes = 0; | 
|  | unsigned modulo = 0; | 
|  | /* parse the arguments */ | 
|  | chp = parse_addr (chp, &level, &space, &addr); | 
|  | if (*chp != ',') | 
|  | { | 
|  | /* let the file autosize */ | 
|  | if (mmap_next_fd == -1) | 
|  | { | 
|  | sim_io_eprintf (sd, "Missing size for memory-region\n"); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  | } | 
|  | else | 
|  | chp = parse_size (chp + 1, &nr_bytes, &modulo); | 
|  | /* old style */ | 
|  | if (*chp == ',') | 
|  | modulo = strtoul (chp + 1, &chp, 0); | 
|  | /* try to attach/insert it */ | 
|  | do_memopt_add (sd, level, space, addr, nr_bytes, modulo, | 
|  | &STATE_MEMOPT (sd), NULL); | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_ALIAS: | 
|  | { | 
|  | char *chp = arg; | 
|  | int level = 0; | 
|  | int space = 0; | 
|  | address_word addr = 0; | 
|  | address_word nr_bytes = 0; | 
|  | unsigned modulo = 0; | 
|  | sim_memopt *entry; | 
|  | /* parse the arguments */ | 
|  | chp = parse_addr (chp, &level, &space, &addr); | 
|  | if (*chp != ',') | 
|  | { | 
|  | sim_io_eprintf (sd, "Missing size for memory-region\n"); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  | chp = parse_size (chp + 1, &nr_bytes, &modulo); | 
|  | /* try to attach/insert the main record */ | 
|  | entry = do_memopt_add (sd, level, space, addr, nr_bytes, modulo, | 
|  | &STATE_MEMOPT (sd), | 
|  | NULL); | 
|  | /* now attach all the aliases */ | 
|  | while (*chp == ',') | 
|  | { | 
|  | int a_level = level; | 
|  | int a_space = space; | 
|  | address_word a_addr = addr; | 
|  | chp = parse_addr (chp + 1, &a_level, &a_space, &a_addr); | 
|  | do_memopt_add (sd, a_level, a_space, a_addr, nr_bytes, modulo, | 
|  | &entry->alias, entry->buffer); | 
|  | } | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_SIZE: | 
|  | { | 
|  | int level = 0; | 
|  | int space = 0; | 
|  | address_word addr = 0; | 
|  | address_word nr_bytes = 0; | 
|  | unsigned modulo = 0; | 
|  | /* parse the arguments */ | 
|  | parse_size (arg, &nr_bytes, &modulo); | 
|  | /* try to attach/insert it */ | 
|  | do_memopt_add (sd, level, space, addr, nr_bytes, modulo, | 
|  | &STATE_MEMOPT (sd), NULL); | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_CLEAR: | 
|  | { | 
|  | fill_byte_value = (uint8_t) 0; | 
|  | fill_byte_flag = 1; | 
|  | return SIM_RC_OK; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_FILL: | 
|  | { | 
|  | unsigned long fill_value; | 
|  | parse_ulong_value (arg, &fill_value); | 
|  | if (fill_value > 255) | 
|  | { | 
|  | sim_io_eprintf (sd, "Missing fill value between 0 and 255\n"); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  | fill_byte_value = (uint8_t) fill_value; | 
|  | fill_byte_flag = 1; | 
|  | return SIM_RC_OK; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_MAPFILE: | 
|  | { | 
|  | if (mmap_next_fd >= 0) | 
|  | { | 
|  | sim_io_eprintf (sd, "Duplicate memory-mapfile option\n"); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  |  | 
|  | mmap_next_fd = open (arg, O_RDWR); | 
|  | if (mmap_next_fd < 0) | 
|  | { | 
|  | sim_io_eprintf (sd, "Cannot open file `%s': %s\n", | 
|  | arg, strerror (errno)); | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  |  | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  | case OPTION_MEMORY_INFO: | 
|  | { | 
|  | sim_memopt *entry; | 
|  | sim_io_printf (sd, "Memory maps:\n"); | 
|  | for (entry = STATE_MEMOPT (sd); entry != NULL; entry = entry->next) | 
|  | { | 
|  | sim_memopt *alias; | 
|  | sim_io_printf (sd, " memory"); | 
|  | if (entry->alias == NULL) | 
|  | sim_io_printf (sd, " region "); | 
|  | else | 
|  | sim_io_printf (sd, " alias "); | 
|  | if (entry->space != 0) | 
|  | sim_io_printf (sd, "0x%lx:", (long) entry->space); | 
|  | sim_io_printf (sd, "0x%08lx", (long) entry->addr); | 
|  | if (entry->level != 0) | 
|  | sim_io_printf (sd, "@0x%lx", (long) entry->level); | 
|  | sim_io_printf (sd, ",0x%lx", | 
|  | (long) entry->nr_bytes); | 
|  | if (entry->modulo != 0) | 
|  | sim_io_printf (sd, "%%0x%lx", (long) entry->modulo); | 
|  | for (alias = entry->alias; | 
|  | alias != NULL; | 
|  | alias = alias->next) | 
|  | { | 
|  | if (alias->space != 0) | 
|  | sim_io_printf (sd, "0x%lx:", (long) alias->space); | 
|  | sim_io_printf (sd, ",0x%08lx", (long) alias->addr); | 
|  | if (alias->level != 0) | 
|  | sim_io_printf (sd, "@0x%lx", (long) alias->level); | 
|  | } | 
|  | sim_io_printf (sd, "\n"); | 
|  | } | 
|  | return SIM_RC_OK; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case OPTION_MAP_INFO: | 
|  | { | 
|  | sim_core *memory = STATE_CORE (sd); | 
|  | unsigned nr_map; | 
|  |  | 
|  | for (nr_map = 0; nr_map < nr_maps; ++nr_map) | 
|  | { | 
|  | sim_core_map *map = &memory->common.map[nr_map]; | 
|  | sim_core_mapping *mapping = map->first; | 
|  |  | 
|  | if (!mapping) | 
|  | continue; | 
|  |  | 
|  | sim_io_printf (sd, "%s maps:\n", map_to_str (nr_map)); | 
|  | do | 
|  | { | 
|  | unsigned modulo; | 
|  |  | 
|  | sim_io_printf (sd, " map "); | 
|  | if (mapping->space != 0) | 
|  | sim_io_printf (sd, "0x%x:", mapping->space); | 
|  | sim_io_printf (sd, "0x%08lx", (long) mapping->base); | 
|  | if (mapping->level != 0) | 
|  | sim_io_printf (sd, "@0x%x", mapping->level); | 
|  | sim_io_printf (sd, ",0x%lx", (long) mapping->nr_bytes); | 
|  | modulo = mapping->mask + 1; | 
|  | if (modulo != 0) | 
|  | sim_io_printf (sd, "%%0x%x", modulo); | 
|  | sim_io_printf (sd, "\n"); | 
|  |  | 
|  | mapping = mapping->next; | 
|  | } | 
|  | while (mapping); | 
|  | } | 
|  |  | 
|  | return SIM_RC_OK; | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | sim_io_eprintf (sd, "Unknown memory option %d\n", opt); | 
|  | return SIM_RC_FAIL; | 
|  |  | 
|  | } | 
|  |  | 
|  | return SIM_RC_FAIL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* "memory" module install handler. | 
|  |  | 
|  | This is called via sim_module_install to install the "memory" subsystem | 
|  | into the simulator.  */ | 
|  |  | 
|  | static MODULE_INIT_FN sim_memory_init; | 
|  | static MODULE_UNINSTALL_FN sim_memory_uninstall; | 
|  |  | 
|  | SIM_RC | 
|  | sim_memopt_install (SIM_DESC sd) | 
|  | { | 
|  | SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | 
|  | sim_add_option_table (sd, NULL, memory_options); | 
|  | sim_module_add_uninstall_fn (sd, sim_memory_uninstall); | 
|  | sim_module_add_init_fn (sd, sim_memory_init); | 
|  | return SIM_RC_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Uninstall the "memory" subsystem from the simulator.  */ | 
|  |  | 
|  | static void | 
|  | sim_memory_uninstall (SIM_DESC sd) | 
|  | { | 
|  | sim_memopt **entry = &STATE_MEMOPT (sd); | 
|  | sim_memopt *alias; | 
|  |  | 
|  | while ((*entry) != NULL) | 
|  | { | 
|  | /* delete any buffer */ | 
|  | if ((*entry)->buffer != NULL) | 
|  | { | 
|  | #ifdef HAVE_MUNMAP | 
|  | if ((*entry)->munmap_length > 0) | 
|  | munmap ((*entry)->buffer, (*entry)->munmap_length); | 
|  | else | 
|  | #endif | 
|  | free ((*entry)->buffer); | 
|  | } | 
|  |  | 
|  | /* delete it and its aliases */ | 
|  | alias = *entry; | 
|  |  | 
|  | /* next victim */ | 
|  | *entry = (*entry)->next; | 
|  |  | 
|  | while (alias != NULL) | 
|  | { | 
|  | sim_memopt *dead = alias; | 
|  | alias = alias->alias; | 
|  | sim_core_detach (sd, NULL, dead->level, dead->space, dead->addr); | 
|  | free (dead); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void sim_dump_memory (SIM_DESC sd); | 
|  |  | 
|  | /* Convenience function for use when debugging the simulator, to be | 
|  | called from within e.g. gdb.  */ | 
|  |  | 
|  | void | 
|  | sim_dump_memory (SIM_DESC sd) | 
|  | { | 
|  | memory_option_handler (sd, NULL, OPTION_MEMORY_INFO, NULL, 0); | 
|  | memory_option_handler (sd, NULL, OPTION_MAP_INFO, NULL, 0); | 
|  | } | 
|  |  | 
|  | static SIM_RC | 
|  | sim_memory_init (SIM_DESC sd) | 
|  | { | 
|  | /* Reinitialize option modifier flags, in case they were left | 
|  | over from a previous sim startup event.  */ | 
|  | fill_byte_flag = 0; | 
|  | mmap_next_fd = -1; | 
|  |  | 
|  | return SIM_RC_OK; | 
|  | } |