/* memory.c -- Memory accessor functions for the AArch64 simulator Copyright (C) 2015-2016 Free Software Foundation, Inc. Contributed by Red Hat. This file is part of GDB. 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 . */ #include "config.h" #include #include #include #include #include "bfd.h" #include "libbfd.h" #include "libiberty.h" #include "elf/internal.h" #include "elf/common.h" #include "memory.h" #include "simulator.h" #include "sim-core.h" static inline void mem_error (sim_cpu *cpu, const char *message, uint64_t addr) { TRACE_MEMORY (cpu, "ERROR: %s: %" PRIx64, message, addr); } #define FETCH_FUNC(RETURN_TYPE, ACCESS_TYPE, NAME, N) \ RETURN_TYPE \ aarch64_get_mem_##NAME (sim_cpu *cpu, uint64_t address) \ { \ return (RETURN_TYPE) sim_core_read_##N (cpu, 0, read_map, address); \ } /* A variant of the FETCH_FUNC macro that uses unaligned reads. The AArch64 only requires 4-byte alignment for 8-byte quantities but the sim common core does not support this. */ #define FETCH_FUNC_U(RETURN_TYPE, ACCESS_TYPE, NAME) \ RETURN_TYPE \ aarch64_get_mem_##NAME (sim_cpu *cpu, uint64_t address) \ { \ return (RETURN_TYPE) sim_core_read_unaligned_8 (cpu, 0, read_map, address); \ } FETCH_FUNC_U (uint64_t, uint64_t, u64) FETCH_FUNC_U (int64_t, int64_t, s64) FETCH_FUNC (uint32_t, uint32_t, u32, 4) FETCH_FUNC (int32_t, int32_t, s32, 4) FETCH_FUNC (uint32_t, uint16_t, u16, 2) FETCH_FUNC (int32_t, int16_t, s16, 2) FETCH_FUNC (uint32_t, uint8_t, u8, 1) FETCH_FUNC (int32_t, int8_t, s8, 1) FETCH_FUNC (float, float, float, 4) FETCH_FUNC_U (double, double, double) void aarch64_get_mem_long_double (sim_cpu *cpu, uint64_t address, FRegister *a) { a->v[0] = sim_core_read_unaligned_8 (cpu, 0, read_map, address); a->v[1] = sim_core_read_unaligned_8 (cpu, 0, read_map, address + 8); } #define STORE_FUNC(TYPE, NAME, N) \ void \ aarch64_set_mem_##NAME (sim_cpu *cpu, uint64_t address, TYPE value) \ { \ TRACE_MEMORY (cpu, \ "write of %" PRIx64 " (%d bytes) to %" PRIx64, \ (uint64_t) value, N, address); \ \ sim_core_write_unaligned_##N (cpu, 0, write_map, address, value); \ } /* A variant of the STORE_FUNC macro that uses unaligned writes. The AArch64 only requires 4-byte alignment for 8-byte quantities but the sim common core does not support this. */ #define STORE_FUNC_U(TYPE, NAME) \ void \ aarch64_set_mem_##NAME (sim_cpu *cpu, uint64_t address, TYPE value) \ { \ TRACE_MEMORY (cpu, \ "write of %" PRIx64 " (8 bytes) to %" PRIx64, \ (uint64_t) value, address); \ \ sim_core_write_unaligned_8 (cpu, 0, write_map, address, value); \ } STORE_FUNC_U (uint64_t, u64) STORE_FUNC_U (int64_t, s64) STORE_FUNC (uint32_t, u32, 4) STORE_FUNC (int32_t, s32, 4) STORE_FUNC (uint16_t, u16, 2) STORE_FUNC (int16_t, s16, 2) STORE_FUNC (uint8_t, u8, 1) STORE_FUNC (int8_t, s8, 1) STORE_FUNC (float, float, 4) STORE_FUNC_U (double, double) void aarch64_set_mem_long_double (sim_cpu *cpu, uint64_t address, FRegister a) { TRACE_MEMORY (cpu, "write of long double %" PRIx64 " %" PRIx64 " to %" PRIx64, a.v[0], a.v[1], address); sim_core_write_unaligned_8 (cpu, 0, write_map, address, a.v[0]); sim_core_write_unaligned_8 (cpu, 0, write_map, address + 8, a.v[1]); } void aarch64_get_mem_blk (sim_cpu * cpu, uint64_t address, char * buffer, unsigned length) { unsigned len; len = sim_core_read_buffer (CPU_STATE (cpu), cpu, read_map, buffer, address, length); if (len == length) return; memset (buffer, 0, length); if (cpu) mem_error (cpu, "read of non-existant mem block at", address); sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu), sim_stopped, SIM_SIGBUS); } const char * aarch64_get_mem_ptr (sim_cpu *cpu, uint64_t address) { char *addr = sim_core_trans_addr (CPU_STATE (cpu), cpu, read_map, address); if (addr == NULL) { mem_error (cpu, "request for non-existant mem addr of", address); sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu), sim_stopped, SIM_SIGBUS); } return addr; } /* We implement a combined stack and heap. That way the sbrk() function in libgloss/aarch64/syscalls.c has a chance to detect an out-of-memory condition by noticing a stack/heap collision. The heap starts at the end of loaded memory and carries on up to an arbitary 2Gb limit. */ uint64_t aarch64_get_heap_start (sim_cpu *cpu) { uint64_t heap = aarch64_get_sym_value ("end"); if (heap == 0) heap = aarch64_get_sym_value ("_end"); if (heap == 0) { heap = STACK_TOP - 0x100000; sim_io_eprintf (CPU_STATE (cpu), "Unable to find 'end' symbol - using addr based " "upon stack instead %" PRIx64 "\n", heap); } return heap; } uint64_t aarch64_get_stack_start (sim_cpu *cpu) { if (aarch64_get_heap_start (cpu) >= STACK_TOP) mem_error (cpu, "executable is too big", aarch64_get_heap_start (cpu)); return STACK_TOP; }