diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ef16258 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 2.8) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) # main (top) cmake dir +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # project specific cmake dir +set(CMAKE_CXX_STANDARD 14) # tODO move up to a general cmake config for all sub projects ? + +# CMake useful variables +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + +# Set the name of your project here +project("iss") + +# Set the version number of your project here (format is MAJOR.MINOR.PATCHLEVEL - e.g. 1.0.0) +set(VERSION_MAJOR "0") +set(VERSION_MINOR "0") +set(VERSION_PATCH "1") +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +include(Common) + +## Git (and its revision) +find_package(Git QUIET) # if we don't find git or FindGit.cmake is not on the system we ignore it. +## The Git module will trigger a reconfiguration for each pull that will bring a new revision on the local repository +set (VCS_REVISION "-1") +if(GIT_FOUND) + include(GetGitRevisionDescription) + get_git_head_revision(GIT_REFSPEC GIT_SHA1) + message(STATUS "GIT branch ${GIT_REFSPEC}") + message(STATUS "GIT revision ${GIT_SHA1}") + set (VCS_REVISION ${GIT_SHA1}) +endif() + +if(DEFINED ENV{LLVM_HOME}) +find_path (LLVM_DIR LLVM-Config.cmake $ENV{LLVM_HOME}/lib/cmake/llvm) +message(STATUS "LLVM_DIR = ${LLVM_DIR}") +endif(DEFINED ENV{LLVM_HOME}) + +find_package(LLVM REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +# This sets the include directory for the reference project. This is the -I flag in gcc. +include_directories( + ${PROJECT_SOURCE_DIR}/incl + ${PROJECT_SOURCE_DIR}/../external/easyloggingpp/src + ${LLVM_INCLUDE_DIRS} +) + +add_dependent_header(util) + +add_definitions(${LLVM_DEFINITIONS}) + +add_subdirectory(src) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..086dfce --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2016, MINRES Technologies GmbH +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e51ee3 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# JIT-ISS +A versatile Just-in-time (JIT) compiling instruction set simulator (ISS) + +**JIT-ISS README** + +This is currently a proof of concept and work in progress, so use at your own risk. It is currently set-up as an Eclipse CDT project and based on LLVM. To build it you need latest LLVM and Eclipse CDT version 4.6 aka Neon. + +To build with SystemC the define WITH_SYSTEMC needs to be set. Then a simple proof-of-concept system is created. Mainly missing are platform peripherals and interrupt handling. It reaches about 5 MIPS in lock-step mode on a MacBook Pro (Core i7-4870HQ@2.5GHz) running in a Docker container. + +JIT-ISS uses libGIS (https://github.com/vsergeev/libGIS) as well as ELFIO (http://elfio.sourceforge.net/), both under MIT license + +**What's missing** + +* only AVR instruction set implemented but not verified + +**Planned features** + +* add platform peripherals + * timers + * gpio + * ext interrupt registers and functionality +* and more + +**Quick start** + +* you need to have a decent compiler, make and cmake installed +* install LLVM 3.9 or 4.0 according to http://apt.llvm.org/ +* download and install SystemC from http://accellera.org/downloads/standards/systemc + * optionally download and install SystemC Verification Library (SCV) from Accelera into the same location +* checkout source from git +* start an out-of-source build like so (e.g. when using LLVM 3.9 and bash) +``` + cd JIT-ISS + mkdir build + cd build + LLVM_HOME=/usr/lib/llvm-3.9 cmake .. + make +``` +* if the SystemC installation is not to be found be cmake you can optionally specify the location by either setting the following environment variables pointing to the installation + - SYSTEMC_HOME + - SYSTEMC_PREFIX + \ No newline at end of file diff --git a/cmake/CPackConfig.cmake b/cmake/CPackConfig.cmake new file mode 100644 index 0000000..806815e --- /dev/null +++ b/cmake/CPackConfig.cmake @@ -0,0 +1,82 @@ +# @author Barthélémy von Haller + +# General CPack configuration +# Info: http://www.itk.org/Wiki/CMake:Component_Install_With_CPack +# _____________________________________________________________________________ + +set(CPACK_PACKAGE_NAME "iss") + +if(APPLE) + set(CPACK_PACKAGE_VENDOR "Organisation") # PackageMaker doesn't like http:// +else() + set(CPACK_PACKAGE_VENDOR "http://example.com") # deb lintian insists on URL +endif() + +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Example project") +set(CPACK_PACKAGE_CONTACT "Person name ") +set(CPACK_PACKAGE_VERSION ${VERSION}) +set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) +set(CPACK_RPM_PACKAGE_DEBUG 0) + +# Select package generator +if(MSVC) + set(CPACK_GENERATOR "NSIS") +endif(MSVC) + +if(APPLE) + set(CPACK_GENERATOR "PackageMaker") + set(CPACK_SET_DESTDIR ON) +endif(APPLE) + +if (${CMAKE_SYSTEM_NAME} MATCHES Linux) + find_program(RPM_EXE rpmbuild) + if(${RPM_EXE} MATCHES RPM_EXE-NOTFOUND) + set(CPACK_GENERATOR "TGZ;DEB") + else() + set(CPACK_GENERATOR "TGZ;DEB;RPM") + endif() +endif(${CMAKE_SYSTEM_NAME} MATCHES Linux) + +# Components +# See www.cmake.org/Wiki/CMake:Component_Install_With_CPack#Controlling_Differents_Ways_of_packaging_components +# _____________________________________________________________________________ +set(CPACK_COMPONENT_INSTALL ON) +set(CPACK_COMPONENTS_ALL libs devel doc) + +set(CPACK_COMPONENT_LIBS_DISPLAY_NAME "Libraries") +set(CPACK_COMPONENT_LIBS_DESCRIPTION "Runtime libraries.") +set(CPACK_COMPONENT_LIBS_GROUP "Runtime") +# express component dependencies this way, it will translate into package dependencies where applicable +set(CPACK_COMPONENT_LIBS_DEPENDS doc) + +set(CPACK_COMPONENT_DOCS_DISPLAY_NAME "Documents") +set(CPACK_COMPONENT_DOCS_DESCRIPTION "User Documentation") +set(CPACK_COMPONENT_DOCS_GROUP "Documentation") + +set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Development files") +set(CPACK_COMPONENT_DEV_DESCRIPTION "Development header files and libraries, as well as cmake config files.") +set(CPACK_COMPONENT_DEV_GROUP "Development") +set(CPACK_COMPONENT_DEV_DEPENDS libs) + +# Debian specific configuration (minimum) +# _____________________________________________________________________________ + +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.1-6), libboost-test-dev") +SET(CPACK_DEBIAN_PACKAGE_CONFLICTS "Hello0-apps") + +# RPM specific configuration (minimum) +# _____________________________________________________________________________ +set(CPACK_RPM_PACKAGE_LICENSE "Proprietary") +set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") +set(CPACK_RPM_PACKAGE_VERSION ${VERSION}) +set(CPACK_RPM_COMPONENT_INSTALL ON) # necessary even if CPACK_COMPONENT_INSTALL set to ON. A bug in my opinion. + +# OS X PackageMaker +# _____________________________________________________________________________ +set(CPACK_OSX_PACKAGE_VERSION "10.5") + +include (InstallRequiredSystemLibraries) +include (CPack) diff --git a/cmake/issConfig.cmake b/cmake/issConfig.cmake new file mode 100644 index 0000000..0b3b2a2 --- /dev/null +++ b/cmake/issConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/issTargets.cmake") diff --git a/incl/iss/arch/traits.h b/incl/iss/arch/traits.h new file mode 100644 index 0000000..faa7f0d --- /dev/null +++ b/incl/iss/arch/traits.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _TRAITS_H_ +#define _TRAITS_H_ + +namespace iss { +namespace arch { + +template +struct traits { + +}; +} +} + +#endif /* _TRAITS_H_ */ diff --git a/incl/iss/arch_if.h b/incl/iss/arch_if.h new file mode 100644 index 0000000..1fa8361 --- /dev/null +++ b/incl/iss/arch_if.h @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _ARCH_IF_H_ +#define _ARCH_IF_H_ + +#include "vm_types.h" + +#include +#include + +namespace iss { +/** + * exception to be thrown if the architecture encounters a synchronous trap + */ +struct trap_access { + trap_access(unsigned id, uint64_t addr) : id(id), addr(addr) {} + const unsigned id; + const uint64_t addr; +}; + +/** + * architecture interface + */ +struct arch_if { + /* deprecated */ + enum operations { + NOP, + AND, OR, XOR, + ADD, ADC, SUB, SUBC, MUL, DIV, + ADDL, ADCL, SUBL, SUBCL, MULL, DIVL, + }; + /** + * execution phases: instruction start and end + */ + enum exec_phase {ISTART, IEND}; + /** + * reset the core + * @param address where to start from + */ + virtual void reset(uint64_t address)=0; + /** + * preload memories from file + * @param name name of th efile to load + * @param type of file, implementation dependent + */ + virtual void load_file(std::string name, int type =-1) = 0; + /** + * notify the core about the execution phase (if needed) + * @param the actual phase the vm is in + */ + virtual void notify_phase(exec_phase) =0; + /** + * get the pointer to the register array + * @return pointer to the registers + */ + virtual uint8_t* get_regs_base_ptr() = 0; + /* deprecated */ + virtual void get_reg(short idx, std::vector& value){}; + virtual void set_reg(short idx, const std::vector& value){}; + /* deprecated */ + virtual bool get_flag(int flag){return false;}; + virtual void set_flag(int flag, bool value){}; + virtual void update_flags(operations op, uint64_t opr1, uint64_t opr2){}; + /** + * read from addresses + * @param addr address to read from, contains access type, address space and address + * @param length length of th edata to read + * @param data pointer to the memory to read into + * @return success or failure of access + */ + virtual iss::status read(const addr_t& addr, unsigned length, uint8_t* const data)=0; + /** + * write to addresses + * @param addr address to read from, contains access type, address space and address + * @param length length of the data to write + * @param data pointer to the memory to write from + * @return success or failure of access + */ + virtual iss::status write(const addr_t& addr, unsigned length, const uint8_t* const data)=0; + /** + * vm encountered a trap (exception, interrupt), process accordingly in core + * @param flags trap flags + * @return new (virtual) address to continue from + */ + virtual uint64_t enter_trap(uint64_t flags){return 0;} + /** + * vm encountered a trap (exception, interrupt), process accordingly in core + * @param flags trap flags + * @param addr address where the trap enountered + * @return new (virtual) address to continue from + */ + virtual uint64_t enter_trap(uint64_t flags, uint64_t addr){return 0;} + /** + * vm decoded the instruction to return from trap (exception, interrupt), process accordingly in core + * @param flags trap flags + * @return new (virtual) address to continue from + */ + virtual uint64_t leave_trap(uint64_t flags){return 0;} + /** + * wait until condition indicated by flags becomes true + * @param flags indicating the condition + */ + virtual void wait_until(uint64_t flags){} + /** + * destructor + */ + virtual ~arch_if() {} + /** + * retrieve information to augment the disassembly + * @return string containing the core status in text form + */ + virtual std::string get_additional_disass_info(){ return "";}; +}; + +} + +#endif /* _ARCH_IF_H_ */ diff --git a/incl/iss/debugger/cmdhandler.h b/incl/iss/debugger/cmdhandler.h new file mode 100644 index 0000000..7a69ba1 --- /dev/null +++ b/incl/iss/debugger/cmdhandler.h @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _CMDHANDLER_H_ +#define _CMDHANDLER_H_ + +#include "encoderdecoder.h" +#include + +#include "server_if.h" +#include "target_adapter_if.h" + +namespace iss { +namespace debugger { + +enum { + MAX_DATABYTES = 0x10000, /* Size of data buffer */ + INOUTBUF_SIZE =2*MAX_DATABYTES+32, + TARGET_SIGNAL0=0, + DBG_PRINTBUF_SIZE=400, + MAXARGS=4 +}; + +enum signals { + /* Signal types */ + HANGUP = 1, + INTERRUPT = 2, + QUIT = 3, + ILLEGAL_INSTRUCTION = 4, + TRAP = 5, + ABORTED = 6, + BUS_ERROR = 7, + FLOATING_POINT_EXCEPTION = 8, + KILLED = 9, + SEGMENT_VIOLATION = 11, + TERMINATED = 15, + STK_FLT = 16 +}; + +/* Remote command */ +#define GEN_ENTRY(name, hlp) {#name, &cmd_handler::rcmd_##name, hlp} + +struct cmd_handler { + /* Table entry definition */ + struct my_custom_command { + /* command name */ + const char *name; + /* command function */ + int (cmd_handler::*function)(int, char **, out_func, data_func); + /* one line of help text */ + const char *help; + }; + + cmd_handler(iss::debugger::server_if& server) + :s(server) + , t(s.get_target()) + , extended_protocol(false) + , can_restart(false) + {} + + void attach(); + + std::string search_memory(const std::string in_buf); + std::string threads(const std::string in_buf); + std::string read_registers(const std::string in_buf); + std::string write_registers(const std::string in_buf); + std::string read_single_register(const std::string in_buf); + std::string write_single_register(const std::string in_buf); + std::string read_memory(const std::string in_buf); + std::string write_memory(const std::string in_buf); + std::string running(const std::string in_buf); + int kill(const std::string in_buf, std::string& out_buf); + std::string thread_alive(const std::string in_buf); + void interrupt_target(); + int restart_target(const std::string in_buf, std::string& out_buf); + std::string detach(const std::string in_buf); + std::string query(const std::string in_buf); + std::string set(const std::string in_buf); + std::string handle_extended(const std::string in_buf); + std::string breakpoint(const std::string in_buf); + int rcmd(const char * const in_buf, out_func of, data_func df); + // TODO: change calls + int rcmd_help(int argc, char *argv[], out_func of, data_func df); + int rcmd_set(int argc, char *argv[], out_func of, data_func df); + + /* Encode return value */ + const char* to_string(int ret){ + switch (ret){ + case iss::Ok: + return "OK"; + case iss::Err: + return "E00"; + case iss::NotSupported: + return ""; + default: + assert(0); + return nullptr; + } + } + const char* to_string(iss::status ret){ + switch (ret){ + case iss::Ok: + return "OK"; + case iss::Err: + return "E00"; + case iss::NotSupported: + return ""; + default: + assert(0); + return nullptr; + } + } + + iss::debugger::server_if& s; + std::shared_ptr t; + encoder_decoder encdec; + bool extended_protocol; + bool can_restart; + std::unordered_map bp_map; + const my_custom_command rp_remote_commands[3] = {/* Table of commands */ + GEN_ENTRY(help, "This help text"), + GEN_ENTRY(set, "Set debug level"), + { nullptr, nullptr, nullptr } //sentinel, end of table marker + }; +}; + +} // namespace debugger +} // namspace iss + +#endif /* _CMDHANDLER_H_ */ diff --git a/incl/iss/debugger/encoderdecoder.h b/incl/iss/debugger/encoderdecoder.h new file mode 100644 index 0000000..148887f --- /dev/null +++ b/incl/iss/debugger/encoderdecoder.h @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ +#ifndef _ENCODERDECODER_H_ +#define _ENCODERDECODER_H_ +#include +#include +#include +#include +#include + +#include "target_adapter_if.h" + +namespace iss { +namespace debugger { + +struct encoder_decoder { + /* Decode/encode functions */ + std::vector dec_data(const char *in); + + int dec_reg(const char *in, unsigned int *reg_no); + + std::vector dec_reg_assignment(const char *in, unsigned int *reg_no); + int dec_mem(const char *in, uint64_t *addr, size_t *len); + int dec_process_query(const char *in, unsigned int *mask, rp_thread_ref *ref); + int dec_break(const char *in, int *type, uint64_t *addr, unsigned int *len); + int dec_list_query(const char *in, int *first, size_t *max, rp_thread_ref *arg); + std::string enc_regs(const std::vector& data, const std::vector& avail); + std::string enc_data(const unsigned char *data, size_t data_len); + std::string enc_data(const std::vector& data); + int enc_string(const char *s, char *out, size_t out_size); + std::string enc_process_query_response(unsigned int mask, const rp_thread_ref *ref, const rp_thread_info *info); + std::string enc_list_query_response(size_t count, int done, const rp_thread_ref& arg, const std::vector found); + int dec_nibble(const char *in, unsigned int *nibble); + int dec_byte(const char *in, unsigned int *byte_ptr); + int dec_4bytes(const char *in, uint32_t *val); + int dec_8bytes(const char *in, uint64_t *val); + int dec_uint32(char const **in, uint32_t *val, char break_char); + int dec_uint64(char const **in, uint64_t *val, char break_char); + void enc_byte(unsigned char val, std::string& out, size_t offset=0); + char enc_byte(unsigned char val, bool highNibble) { + return highNibble?hex[(val >> 4) & 0xf]:hex[val & 0xf]; + } + +private: + const char* hex = "0123456789abcdef"; + void encode_str(std::stringstream& ss, const char* const str); +}; + +} // namespace debugger +} // namspace iss +#endif /* _ENCODERDECODER_H_ */ diff --git a/incl/iss/debugger/gdb_session.h b/incl/iss/debugger/gdb_session.h new file mode 100755 index 0000000..f1d9488 --- /dev/null +++ b/incl/iss/debugger/gdb_session.h @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _GDB_SESSION_H_ +#define _GDB_SESSION_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "cmdhandler.h" +#include "serialized_connection.h" +#include "server_if.h" + +namespace iss { +namespace debugger { + +using boost::asio::ip::tcp; + +class gdb_session: public connection::async_listener { +public: + gdb_session(server_if* server_, boost::asio::io_service& io_service) + : server(server_) + , conn_shptr(new connection(io_service)) + , handler(*server) + { + } + + virtual ~gdb_session() { + } + + tcp::socket& socket() { + return conn_shptr->socket(); + } + + int start(); + + void receive_completed(const boost::system::error_code& e, std::string* data) override; + + void send_completed(const boost::system::error_code& e) override; + + bool message_completed(std::vector& buffer)override; +protected: + + std::string check_packet(std::string& msg); + + void parse_n_execute(std::string& msg); + + void respond(const std::string msg){ + last_msg=msg; + //std::this_thread::sleep_for(std::chrono::milliseconds(2)); + CLOG(TRACE, "connection")<<"Processed message, responding with '"<write_data(&last_msg); + conn_shptr->async_read(); + } +private: + server_if* server; + boost::shared_ptr > conn_shptr; + std::string last_msg; + cmd_handler handler; +}; +} +} +#endif /* _GDB_SESSION_H_ */ diff --git a/incl/iss/debugger/serialized_connection.h b/incl/iss/debugger/serialized_connection.h new file mode 100755 index 0000000..4509fdf --- /dev/null +++ b/incl/iss/debugger/serialized_connection.h @@ -0,0 +1,410 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _SERIALIZED_CONNECTION_H_ +#define _SERIALIZED_CONNECTION_H_ + +#include + +#ifdef USE_TEXT +#include +#include +typedef boost::archive::text_oarchive oarchive_type; +typedef boost::archive::text_iarchive iarchive_type; +#else +#include +#include +typedef boost::archive::binary_oarchive oarchive_type; +typedef boost::archive::binary_iarchive iarchive_type; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/// The connection class provides serialization primitives on top of a socket. +/** + * Each message sent using this class consists of: + * @li An 8-byte header containing the length of the serialized data in + * hexadecimal. + * @li The serialized data. + */ +template +class connection: public boost::enable_shared_from_this > { +public: + typedef boost::shared_ptr > ptr; + + struct async_listener: public boost::enable_shared_from_this{ + virtual void send_completed(const boost::system::error_code& error) = 0; + virtual void receive_completed(const boost::system::error_code& error, TREQ* result) = 0; + }; + /// Constructor. + connection(boost::asio::io_service& io_service) : socket_(io_service) { + } + /// Get the underlying socket. Used for making a connection or for accepting + /// an incoming connection. + boost::asio::ip::tcp::socket& socket() { + return socket_; + } + /// + void add_listener(boost::shared_ptr l){ + listener=l; + } + /// + void remove_listener(){ + listener.reset(); + } + /// Asynchronously write a data structure to the socket. + void async_write(TRESP& t) { + async_write(&t); + } + /// Asynchronously write a data structure to the socket. + void async_write(TRESP* t) { + // Serialize the data first so we know how large it is. + std::ostringstream archive_stream; + oarchive_type archive(archive_stream); + archive << t; + outbound_data_ = archive_stream.str(); +#ifdef EXTENDED_TRACE + CLOG(TRACE, "connection")<<"outbound async data with len "<< outbound_data_.size()<<":'"<send_completed(err); + return; + } + outbound_header_ = header_stream.str(); + // Write the serialized data to the socket. We use "gather-write" to send + // both the header and the data in a single write operation. + std::vector buffers; + buffers.push_back(boost::asio::buffer(outbound_header_)); + buffers.push_back(boost::asio::buffer(outbound_data_)); + boost::asio::async_write(socket_, buffers, + boost::bind(&connection::handle_async_write, this->shared_from_this(), + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + +protected: + /// notify the listener about the finish of the send process + void handle_async_write(const boost::system::error_code& err, size_t /*bytes_transferred*/){ + if(listener!=NULL) listener->send_completed(err); + } +public: + /// Asynchronously read a data structure from the socket. + void async_read() { + // Issue a read operation to read exactly the number of bytes in a header. + boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_), + boost::bind(&connection::async_read_header, this, boost::asio::placeholders::error)); + } + +protected: + /// Handle a completed read of a message header. The handler is passed using + /// a tuple since boost::bind seems to have trouble binding a function object + /// created using boost::bind as a parameter. + void async_read_header(const boost::system::error_code& e) { + if (e) { + if(listener) listener->receive_completed(e, NULL); + } else { + // Determine the length of the serialized data. + std::istringstream is(std::string(inbound_header_, header_length)); + std::size_t inbound_data_size = 0; + if (!(is >> std::hex >> inbound_data_size)) { + // Header doesn't seem to be valid. Inform the caller. + boost::system::error_code error(boost::asio::error::invalid_argument); + if(listener) listener->receive_completed(e, NULL); + return; + } + // Start an asynchronous call to receive the data. + inbound_data_.resize(inbound_data_size); + boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_), + boost::bind(&connection::async_read_data, this, boost::asio::placeholders::error)); + } + } + + /// Handle a completed read of message data. + void async_read_data(const boost::system::error_code& e) { + if (e) { + if(listener) listener->receive_completed(e, NULL); + }else { + // Extract the data structure from the data just received. + try { + std::string archive_data(&inbound_data_[0], inbound_data_.size()); +#ifdef EXTENDED_TRACE + CLOG(TRACE, "connection")<<"inbound async data with len "<< inbound_data_.size()<<":'"<> t; + if(listener) listener->receive_completed(e, t); + } catch (std::exception& /* unnamed */) { + // Unable to decode data. + boost::system::error_code err(boost::asio::error::invalid_argument); + if(listener) listener->receive_completed(err, NULL); + return; + } + } + } + +public: + /// + void write_data(boost::shared_ptr& t){ + write_data(t.get()); + } + + void write_data(TRESP& t){ + write_data(&t); + } + + void write_data(TRESP* t){ + boost::system::error_code ec; + this->write_data(t, ec); + boost::asio::detail::throw_error(ec); + } + + void write_data(TRESP* t, boost::system::error_code& ec){ + // Serialize the data first so we know how large it is. + std::ostringstream archive_stream; + oarchive_type archive(archive_stream); + archive << t; + outbound_data_ = archive_stream.str(); +#ifdef EXTENDED_TRACE + CLOG(TRACE, "connection")<<"outbound sync data with len "<< outbound_data_.size()<<":'"< buffers; + buffers.push_back(boost::asio::buffer(outbound_header_)); + buffers.push_back(boost::asio::buffer(outbound_data_)); + boost::asio::write(socket_, buffers); + } + + void read_data(boost::shared_ptr& msg){ + TREQ* m; + read_data(m); + msg.reset(m); + } + + void read_data(TREQ*& t){ + boost::system::error_code ec; + this->read_data(t, ec); + boost::asio::detail::throw_error(ec); + } + + void read_data(TREQ*& t, boost::system::error_code& ec){ + boost::asio::read(socket_, boost::asio::buffer(inbound_header_, header_length), boost::asio::transfer_exactly(header_length)); + // Determine the length of the serialized data. + std::istringstream is(std::string(inbound_header_, header_length)); + std::size_t inbound_data_size = 0; + if (!(is >> std::hex >> inbound_data_size)) { + // Header doesn't seem to be valid. Inform the caller. + ec.assign(boost::asio::error::invalid_argument,boost::system::get_system_category()); + return; + } + // Start an synchronous call to receive the data. + inbound_data_.resize(inbound_data_size); + boost::asio::read(socket_, boost::asio::buffer(inbound_data_, inbound_data_size), boost::asio::transfer_exactly(inbound_data_size)); + std::string archive_data(&inbound_data_[0], inbound_data_.size()); +#ifdef EXTENDED_TRACE + CLOG(TRACE, "connection")<<"inbound sync data with len "<< inbound_data_.size()<<":'"<> t; + return; + } + +private: + /// The underlying socket. + boost::asio::ip::tcp::socket socket_; + /// The size of a fixed length header. + enum { + header_length = 8 + }; + /// Holds an outbound header. + std::string outbound_header_; + /// Holds the outbound data. + std::string outbound_data_; + /// Holds an inbound header. + char inbound_header_[header_length]; + /// Holds the inbound data. + std::vector inbound_data_; + + boost::shared_ptr listener; +}; + +template<> +class connection: public boost::enable_shared_from_this > { +public: + struct async_listener: public boost::enable_shared_from_this{ + virtual void send_completed(const boost::system::error_code& error) = 0; + virtual void receive_completed(const boost::system::error_code& error, std::string* result) = 0; + virtual bool message_completed(std::vector& buffer) {return true;}; + }; + /// Constructor. + connection(boost::asio::io_service& io_service) : socket_(io_service) { + } + /// Get the underlying socket. Used for making a connection or for accepting + /// an incoming connection. + boost::asio::ip::tcp::socket& socket() { + return socket_; + } + /// + void add_listener(boost::shared_ptr l){ + listener=l; + } + /// template specialization for std::string to allow plain communication + /// Asynchronously write a string to the socket. + void async_write(const std::string& str) { + // Serialize the data first so we know how large it is. + outbound_data_ = str; +#ifdef EXTENDED_TRACE + LOG(TRACE)<<"outbound async data with len "<< outbound_data_.size()<<":'"< buffers; + buffers.push_back(boost::asio::buffer(outbound_data_)); + boost::asio::async_write(socket_, buffers, + boost::bind(&connection::handle_async_write, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + +protected: + /// notify the listener about the finish of the send process + void handle_async_write(const boost::system::error_code& err, size_t /*bytes_transferred*/){ + if(listener!=NULL) listener->send_completed(err); + } +public: + /// template specialization for std::string to allow plain communication + /// Asynchronously read a string from the socket. + void async_read() { + // Issue a read operation to read exactly the number of bytes in a header. + boost::asio::async_read(socket_, boost::asio::buffer(inbound_buffer_), + boost::bind(&connection::async_read_data, this, boost::asio::placeholders::error)); + } +protected: + /// template specialization for std::string to allow plain communication + /// Handle a read of string data. + void async_read_data(const boost::system::error_code& e) { + if (e) { + if(listener) listener->receive_completed(e, NULL); + }else { + // Extract the data structure from the data just received. + try { + inbound_data_.push_back(inbound_buffer_[0]); + if(listener->message_completed(inbound_data_)){ + std::string receive_data(&inbound_data_[0], inbound_data_.size()); +#ifdef EXTENDED_TRACE + LOG(TRACE)<<"inbound async data with len "<< inbound_data_.size()<<":'"<receive_completed(e, &receive_data); + inbound_data_.clear(); + } else + boost::asio::async_read(socket_, boost::asio::buffer(inbound_buffer_), + boost::bind(&connection::async_read_data, this, boost::asio::placeholders::error)); + } catch (std::exception& /* unnamed */) { + // Unable to decode data. + boost::system::error_code err(boost::asio::error::invalid_argument); + if(listener) listener->receive_completed(err, NULL); + return; + } + } + } +public: + /// + void write_data(const std::string* t){ + boost::system::error_code ec; + this->write_data(t, ec); + boost::asio::detail::throw_error(ec); + } + + void write_data(const std::string* t, boost::system::error_code& ec){ + outbound_data_ = *t; +#ifdef EXTENDED_TRACE + LOG(TRACE)<<"outbound sync data with len "<< outbound_data_.size()<<":'"< buffers; + buffers.push_back(boost::asio::buffer(outbound_data_)); + boost::asio::write(socket_, buffers); + } + + void read_data(std::string*& t){ + boost::system::error_code ec; + this->read_data(t, ec); + boost::asio::detail::throw_error(ec); + } + + void read_data(std::string*& t, boost::system::error_code& ec){ + boost::asio::read(socket_, boost::asio::buffer(inbound_buffer_, buffer_length)); + t=new std::string(inbound_buffer_); + return; + } + +private: + /// The underlying socket. + boost::asio::ip::tcp::socket socket_; + /// The size of a fixed length header. + enum { + buffer_length = 1 + }; + /// Holds the outbound data. + std::string outbound_data_; + /// Holds an inbound header. + char inbound_buffer_[buffer_length]; + /// Holds the inbound data. + std::vector inbound_data_; + + boost::shared_ptr listener; +}; +#endif /* _SERIALIZED_CONNECTION_H_ */ diff --git a/incl/iss/debugger/server.h b/incl/iss/debugger/server.h new file mode 100644 index 0000000..d92b83f --- /dev/null +++ b/incl/iss/debugger/server.h @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _SERVER_H_ +#define _SERVER_H_ + +#include +#include +#include +#include +#include "server_base.h" + +namespace iss { +namespace debugger { + +template +class server: public server_base { +public: + + static void run_server(iss::debugger_if* vm, unsigned int port) { + if(get()!=NULL) { LOG(FATAL)<< "server already initialized"; } + LOG(DEBUG) << "starting server"; + get(new server(vm, port)); + } + + static server* get(){return get(NULL);} + + unsigned short get_port_nr(){ + return acceptor.local_endpoint().port(); + } + + void shutdown() override { + delete work_ctrl; + work_ctrl=nullptr; + // Wait for all threads in the pool to exit. + threads.join_all(); + // allow the synchronizer to run + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + +protected: + static server* get(server* srv){ + static server* s = NULL; + if(srv != NULL && s==NULL) s = srv; + return s; + } + + server(iss::debugger_if* vm, unsigned short port) + : server_base(vm) + , acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) + { + // Create a pool of threads to run all of the io_services. + std::size_t thread_pool_size_ = 2; + work_ctrl = new boost::asio::io_service::work(io_service); + boost::thread::hardware_concurrency(); + for (std::size_t i = 0; i < thread_pool_size_; ++i){ + threads.create_thread([this]()->void {io_service.run();}); + } + createNewSession(); + }; + + void createNewSession(){ + boost::shared_ptr new_session(new SESSION(this, acceptor.get_io_service())); + acceptor.async_accept(new_session->socket(), + boost::bind(&server::handle_accept, this, boost::asio::placeholders::error, new_session)); + } + + void shutDown(){ + // request shutdown of simulation + server_base::shutdown(); + // stop the io acceptor + acceptor.get_io_service().stop(); + } + +private: + /// Handle completion of a accept operation by starting a session. + void handle_accept(const boost::system::error_code& e, boost::shared_ptr session) { + if (!e){ + // Start an accept operation for a new connection. If it finishes create a new session + if(!session->start()) createNewSession(); + } else { + // An error occurred. Log it and return. Since we are not starting a new + // accept operation the io_service will run out of work to do and the + // thread will exit. + LOG(ERROR) << e.message(); + } + } + // server related members + boost::asio::io_service io_service; + boost::asio::io_service::work *work_ctrl; + boost::asio::ip::tcp::acceptor acceptor; + boost::thread_group threads; + +}; + +} // namespace debugger +} // namspace iss + +#endif /* _SERVER_H_ */ diff --git a/incl/iss/debugger/server_base.h b/incl/iss/debugger/server_base.h new file mode 100644 index 0000000..9eb8829 --- /dev/null +++ b/incl/iss/debugger/server_base.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _SERVER_BASE_H_ +#define _SERVER_BASE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include "server_if.h" +#include + +namespace iss { +namespace debugger { + +struct server_base : public server_if{ + + server_base(debugger_if* adapter); + + ~server_base(); + + unsigned int get_reg_width(int index) const ; + + void run(unsigned coreId) override ; + + void request_stop(unsigned coreId) override ; + + void wait_for_stop() override; + + void step(unsigned coreId, unsigned steps=1) override ; + + iss::status reset(int coreId); + + target_adapter_if* get_target() override { return vm->accquire_target_adapter(this);} + +protected: + debugger_if* vm; + int dummy_func(); + target_adapter_if* tgt; +}; + +} // namespace debugger +} // namspace iss + +#endif /* _SERVER_BASE_H_ */ diff --git a/incl/iss/debugger/server_if.h b/incl/iss/debugger/server_if.h new file mode 100755 index 0000000..d2fff06 --- /dev/null +++ b/incl/iss/debugger/server_if.h @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _SERVER_IF_H_ +#define _SERVER_IF_H_ + +#include +#include + +#include +#include +#include +#include +#include + + +namespace iss { + +struct debugger_if; + +namespace debugger { +// forward declaration +struct target_adapter_if; +/** + * the debug server interface + */ +struct server_if{ + friend class iss::debugger_if; + /** + * debugger access type + */ + enum access_type {Read, Write}; + /** + * type of breakpoints + */ + enum bp_type {OnExec=0, OnRead=1, OnWrite=2, OnRW=3}; + /** + * address type + */ + enum addr_type {Phys, Virt, Log}; + /** + * execution mode of debuggee + */ + enum mode_e {MODE_RUN, MODE_BREAK, MODE_STOP}; + + /** + * destructor + */ + virtual ~server_if(){}; + /** + * run the specified core, numeric_limits::max() indicates all cores + * @param coreId the core to let run + */ + virtual void run(unsigned coreId) = 0; + /** + * stop the specified core (non-blocking) + * @param coreId the core to stop + */ + virtual void request_stop(unsigned coreId) = 0; + /** + * check if a core is executing + * @return true if the core runs + */ + virtual bool is_running(){ return mode.load()!=MODE_STOP;} + /** + * wait until request_stop() is processed and the specified core stops + */ + virtual void wait_for_stop() = 0; + /** + * single step the specified core + * @param coreId the core to single step + * @param steps number of steps to execute + */ + virtual void step(unsigned coreId, unsigned steps=1) = 0; + /** + * shut down the simulation and server + */ + virtual void shutdown() = 0; + /** + * reset the core and system + * @param coreId core to reset + * @return result of the operation + */ + virtual status reset(int coreId) = 0; + + enum exec_states { initialized=0, running=1, stopped=2, stepping=3}; + /** + * get the target adapter for the core being debugged + * @return + */ + virtual target_adapter_if* get_target() = 0; + /** + * check if the simulation can continue + * @param bp_handle the handle of breakpoint condition being met, 0 means no hit + */ + inline + void check_continue(unsigned bp_handle) { + if(bp_handle){ + mode.store(MODE_STOP, std::memory_order_release); + last_bp=bp_handle; + } + while(mode.load(std::memory_order_acquire)==MODE_STOP){ + syncronizer.executeNext(); + } + if(--cycles==0) mode=MODE_STOP; + } + /** + * execute the specified function synchronized in the simulation thread + * @param f function to execute + * @param args arguments of the function + * @return result value of function (if any) + */ + template + typename std::result_of::type execute_syncronized(F&& f, Args&&... args) { + return syncronizer.enqueue_and_wait(f, args...); + } + +protected: + thread_syncronizer syncronizer; + std::atomic mode{MODE_STOP}; + std::atomic cycles; + unsigned last_bp; +}; + +} // namespace debugger +} // namspace iss + +#endif /* _SERVER_IF_H_ */ diff --git a/incl/iss/debugger/target_adapter_base.h b/incl/iss/debugger/target_adapter_base.h new file mode 100644 index 0000000..1f45ca9 --- /dev/null +++ b/incl/iss/debugger/target_adapter_base.h @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _TARGET_ADAPTER_BASE_H_ +#define _TARGET_ADAPTER_BASE_H_ + +#include "target_adapter_if.h" +#include +#include +#include + +namespace iss { +namespace debugger { + +struct target_adapter_base: public target_adapter_if { + + + target_adapter_base(iss::debugger::server_if* srv) + : srv(srv) + , bp_lut(0) + { + + } + + void set_server(iss::debugger::server_if* server){srv=server;} + + void check_continue(uint64_t pc){ + unsigned handle = bp_lut.getEntry(pc); + srv->check_continue(handle); + } + + /* return table of remote commands */ + const std::vector& custom_commands() override { return ccmds;} + + void help(const char *prog_name) override; + + iss::status open(int argc, char * const agrv[], const char *prog_name, log_func log_fn) override; + + void close(void) override; + + iss::status connect(std::string& status_string, bool& can_restart) override; + + iss::status disconnect(void) override; + + void kill(void) override; + + iss::status restart(void) override; + + void stop(void) override; + + iss::status resume_from_current(bool step, int sig) override; + + iss::status wait_non_blocking(std::string& status_string, out_func out, bool& running) override; + + iss::status wait_blocking(std::string& status_string, out_func out) override; + +protected: + iss::debugger::server_if* srv; + util::range_lut bp_lut; + long bp_count=0; + std::vector ccmds; +}; + +} // namespace debugger +} // namspace iss + +#endif /* _TARGETADAPTER_H_ */ diff --git a/incl/iss/debugger/target_adapter_if.h b/incl/iss/debugger/target_adapter_if.h new file mode 100644 index 0000000..b0edd5b --- /dev/null +++ b/incl/iss/debugger/target_adapter_if.h @@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _TARGET_ADAPTER_IF_H_ +#define _TARGET_ADAPTER_IF_H_ + +#include +#include +#include + +namespace iss { +namespace debugger { + +/* Function to do console output from wait methods */ +typedef void (*out_func)(const char *string); + +/* Function to transefer data received as qRcmd response */ +typedef void (*data_func)(const char *string); + +/* Function to do logging */ +typedef void (*log_func)(int level, const char *string, ...); + +struct rp_thread_ref +{ + uint64_t val; +} ; + +struct rp_thread_info { + rp_thread_ref thread_id; + int exists; + char display[256]; + char thread_name[32]; + char more_display[256]; +}; + +struct target_adapter_if { + + /* Table entry definition */ + struct custom_command { + /* command name */ + const char *name; + /* command function */ + int (*function)(int, char **, out_func, data_func); + /* one line of help text */ + const char *help; + }; + + virtual ~target_adapter_if(){} + + /* return table of remote commands */ + virtual const std::vector& custom_commands() = 0; + + /*====================== Help/Debug =======================*/ + + /* Help, argument is a pointer to itself */ + virtual void help(const char *prog_name) = 0; + + /*========= Open/Close/Connect/Disconnect ==============*/ + + /* Start target stub and provide run time parameters + in time tested manner, does not assume actually + connecting to target. */ + virtual iss::status open(int argc, char * const agrv[], const char *prog_name, log_func log_fn) = 0; + + /* Close target stub: if target is still connected disconnect and + leave it running */ + virtual void close(void) = 0; + + /* Actually connect to a target and return status string; */ + virtual iss::status connect(std::string& status_string, bool& can_restart) = 0; + + /* Disconnect from a target a leave it running */ + virtual iss::status disconnect(void) = 0; + + /*=================== Start/Stop =========================*/ + + /* Kill target: disconnect from a target and leave it waiting + for a command. Target output is ignored. + + Restart: start target all over again. + + Stop: break into running target + + Note these commands are used in following sequences only + + 1. kill, close, terminate proxy + 2. kill, restart, connect + 3. restart, connect + 4. stop, wait */ + + /* Kill target: disconnect from a target and leave it waiting + for a command. It is expected that either close or wait or + connect will follow after kill to get last status_string */ + virtual void kill(void) = 0; + + /* Restart target and return status string */ + virtual iss::status restart(void) = 0; + + /* Stop target. E.g. send ^C or BREAK to target - note + it has to be followed either by wait or connect in order to + to get last status_string */ + virtual void stop(void) = 0; + + /*============== Thread Control ===============================*/ + + /* Set generic thread */ + virtual iss::status set_gen_thread(rp_thread_ref& thread) = 0; + + /* Set control thread */ + virtual iss::status set_ctrl_thread(rp_thread_ref& thread) = 0; + + /* Get thread status */ + virtual iss::status is_thread_alive(rp_thread_ref& thread, bool& alive) = 0; + + /*============= Register Access ================================*/ + + /* Read all registers. buf is 4-byte aligned and it is in + target byte order. If register is not available + corresponding bytes in avail_buf are 0, otherwise + avail buf is 1 */ + virtual iss::status read_registers(std::vector& data_buf, std::vector& avail_buf) = 0; + + /* Write all registers. buf is 4-byte aligned and it is in target + byte order */ + virtual iss::status write_registers(const std::vector& buf) = 0; + + /* Read one register. buf is 4-byte aligned and it is in + target byte order. If register is not available + corresponding bytes in avail_buf are 0, otherwise + avail buf is 1 */ + virtual iss::status read_single_register(unsigned int reg_no, std::vector& buf, std::vector& avail_buf) = 0; + + /* Write one register. buf is 4-byte aligned and it is in target byte + order */ + virtual iss::status write_single_register(unsigned int reg_no, const std::vector& buf) = 0; + + /*=================== Memory Access =====================*/ + + /* Read memory, buf is 4-bytes aligned and it is in target + byte order */ + virtual iss::status read_mem(uint64_t addr, std::vector& buf) = 0; + + /* Write memory, buf is 4-bytes aligned and it is in target + byte order */ + virtual iss::status write_mem(uint64_t addr, const std::vector& buf) = 0; + + /*================ Resume/Wait ============================*/ + + /* Resume from current address, if not supported it has to be figured out by wait */ + virtual iss::status resume_from_current(bool step, int sig) = 0; + + /* Resume from specified address, if not supported it + has to be figured out by wait */ + virtual iss::status resume_from_addr(bool step, int sig, uint64_t addr) = 0; + + /* Wait function, wait_partial is called by the proxy with one + tick intervals, so it allows to break into running + target */ + + /* Check for event and return. It allows proxy server to + check messages from gdb allowing gdb to stop/kill target. + Break and kill commands are generated by a human being so, + the process can wait inside wait_partial with some substantial + timeouts. It seems like 1s time will be highest acceptable value. + + In this case return value RP_TARGETRET_NOSUPP means, that + response to previous resume was - 'not supported'. If this operation + is not implemented by target, then it will return OK and + implemeted will be 0. + + status_string is unchanged unless return value is OK and + implemented is non 0 */ + virtual iss::status wait_non_blocking(std::string& status_string, out_func out, bool& running){ + return iss::NotSupported; + } + + /* Wait for event, fill (null-terminated) status_string upon successful + return, if there is not enough space for 'TAA... string' use + 'SAA' instead, status_sting_len is always > 3 + + In this case return value RP_TARGETRET_NOSUPP means, that + response to previous resume was - 'not supported'. If this operation + is not implemented by target, then it will return OK and + implemeted will be 0 + + status_string is unchanged unless return value is OK and + implemented is non 0 */ + virtual iss::status wait_blocking(std::string& status_string, out_func out) = 0; + + /*============= Queries ===============================*/ + + /* Bits of mask determine set of information about thread + to be retrieved, results are put into info. */ + virtual iss::status process_query(unsigned int& mask, const rp_thread_ref& arg, rp_thread_info& info) = 0; + + /* List threads. If first is non-zero then start from the first thread, + otherwise start from arg, result points to array of threads to be + filled out, result size is number of elements in the result, + num points to the actual number of threads found, done is + set if all threads are processed. */ + virtual iss::status thread_list_query(int first, const rp_thread_ref& arg, std::vector& result, size_t max_num, size_t& num, bool& done) = 0; + + /* Query current thread id */ + virtual iss::status current_thread_query(rp_thread_ref& thread) = 0; + + /* Query offset of major sections in memory */ + virtual iss::status offsets_query(uint64_t& text, uint64_t& data, uint64_t& bss) = 0; + + /* Query crc32 of memory area */ + virtual iss::status crc_query(uint64_t addr, size_t len, uint32_t& val) = 0; + + /* Raw query, see gdb-XXXX/gdb/remote.c. we got buffer + call this function. It is a responsibility of the target + to fill out out_buf correctly in case of success. + + It is planned to have more more specific queries in + the nearest future. */ + virtual iss::status raw_query(std::string in_buf, std::string& out_buf) = 0; + + /*============ Breakpoints ===========================*/ + /** + * add a breakpoint + * + * @param type the type of the breakpoint: 0 - sw exec, 1 - hw exec, 2 - write watchpoint, 3 - access watchpoint + * @param addr address of the breakpoint + * @param length length of the range to check + * @return iss:Ok if successful, iss::Err otherwise + */ + virtual iss::status add_break(int type, uint64_t addr, unsigned int length) = 0; + /** + * remove a breakpoint + * + * @param type the type of the breakpoint: 0 - sw exec, 1 - hw exec, 2 - write watchpoint, 3 - access watchpoint + * @param addr address of the breakpoint + * @param length length of the range to check + * @return iss:Ok if successful, iss::Err otherwise + */ + virtual iss::status remove_break(int type, uint64_t addr, unsigned int length) = 0; + + /* Query thread info */ + virtual iss::status threadinfo_query(int first, std::string& out_buf) = 0; + + /* Query thread extra info */ + virtual iss::status threadextrainfo_query(const rp_thread_ref& thread, std::string& out_buf) = 0; + + /* Query packet size */ + virtual iss::status packetsize_query(std::string& out_buf) = 0; +}; + +} // namespace debugger +} // namspace iss + +#endif /* _TARGET_ADAPTER_H_ */ diff --git a/incl/iss/debugger_if.h b/incl/iss/debugger_if.h new file mode 100644 index 0000000..dc4a455 --- /dev/null +++ b/incl/iss/debugger_if.h @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _DEBUGGER_IF_H_ +#define _DEBUGGER_IF_H_ + +#include "vm_types.h" +#include +#include "debugger/server_if.h" +#include "debugger/target_adapter_if.h" + +namespace iss { + +struct debugger_if { + /** + * destructor + */ + virtual ~debugger_if(){}; + /** + * check if debugging is enabled + * @return + */ + inline bool debugging_enabled(){return dbg_enabled;} + /** + * return a target adapter for accessing the core to be debugged + * @param server server interface to be used for the target adapter + * @return the target adapter for the core + */ + virtual debugger::target_adapter_if* accquire_target_adapter(debugger::server_if* server) = 0; + +protected: + bool dbg_enabled=false; +}; +} + + +#endif /* _DEBUGGER_IF_H_ */ diff --git a/incl/iss/iss.h b/incl/iss/iss.h new file mode 100644 index 0000000..af6ddad --- /dev/null +++ b/incl/iss/iss.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _ISS_H +#define _ISS_H + +#include +#include +#include + +namespace iss { + +template +std::unique_ptr create(std::string inst_name, bool dump=false); + +template +std::unique_ptr create(std::string inst_name, unsigned short port, bool dump=false); + +template +std::unique_ptr create(ARCH*, bool dump=false); + +template +std::unique_ptr create(ARCH*, unsigned short port, bool dump=false); + +} + +#endif /* _ISS_H */ diff --git a/incl/iss/jit/MCJIThelper.h b/incl/iss/jit/MCJIThelper.h new file mode 100644 index 0000000..eff2441 --- /dev/null +++ b/incl/iss/jit/MCJIThelper.h @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _MCJITHELPER_H_ +#define _MCJITHELPER_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace iss { +/** + * get the LLVM context + * NOTE: this is a singleton and not threadsave + * @return the cotext + */ +llvm::LLVMContext& getContext(); +/** + * initialize the LLVM infrastructure + * @param argc the number of CLI arguments + * @param argv the array of CLI arguments + */ +void init_jit(int argc, char *argv[]); + + +namespace vm { +/** + * MCJIT helper class + */ +struct MCJIT_helper { + /** + * constructor + * @param context the LLVM context to use + * @param dump dump the generated IR per module + */ + MCJIT_helper(llvm::LLVMContext& context, bool dump=false); + /** + * destructor + */ + ~MCJIT_helper(); + /** + * generate a unique name from a character array using internal static counter + * @param root + * @return the generated name + */ + std::string GenerateUniqueName(const char *root) const; + /** + * Generate a unique name based on str by appending mod in hex notation + * @param str the base string + * @param mod the modification count + */ + void GenerateUniqueName(std::string &str, uint64_t count) const; + /** + * create a module with all global function declarations + * @return the module pointer + */ + std::unique_ptr createModule(); + /** + * compile the function named of module mod + * @param mod the module to be compiled + * @param name the name of the toplevel entry function + */ + void *getPointerToFunction(std::unique_ptr mod, const std::string &name); + +protected: + llvm::ExecutionEngine *compileModule(std::unique_ptr mod); + + void add_functions_2_module(llvm::Module* mod); + +private: + llvm::LLVMContext& context; + std::unordered_map engineMap; + const bool dumpEnabled; +}; +/** + * template wrapper to get get rid of casting in code + */ +template +struct MCJIT_arch_helper: public MCJIT_helper { + typedef typename arch::traits::addr_t (*fPtr_t)(); + /** + * constructor + * @param context the LLVM context + * @param dump dump the generated IR per module + */ + MCJIT_arch_helper(llvm::LLVMContext& context, bool dump=false):MCJIT_helper(context, dump){} + + /** + * wrapper to get correctly typed function pointer from compilation + * @param m the module to compile + * @param f the toplevel entry function of the module + * @return the function pointer + */ + fPtr_t getPointerToFunction(std::unique_ptr m, const llvm::Function* f){ + return (fPtr_t)MCJIT_helper::getPointerToFunction(std::move(m), f->getName()); + } +}; + +} +} +#endif /* _MCJITHELPER_H_ */ diff --git a/incl/iss/vm_base.h b/incl/iss/vm_base.h new file mode 100644 index 0000000..e9db9b0 --- /dev/null +++ b/incl/iss/vm_base.h @@ -0,0 +1,507 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _VM_BASE_H_ +#define _VM_BASE_H_ + +#include "vm_if.h" +#include "debugger_if.h" +#include "debugger/target_adapter_base.h" +#include "arch_if.h" +#include "arch/traits.h" +#include "util/ities.h" +#include "util/range_lut.h" +#include "jit/MCJIThelper.h" + +#include + +#include +#include +#include +#include +#include "llvm/IR/MDBuilder.h" + +#include +#include +#include +#include +#include +#include + +namespace iss { + +template +class sim; + +namespace vm { + +extern llvm::cl::opt LikelyBranchWeight; +extern llvm::cl::opt UnlikelyBranchWeight; + +enum continuation_e {CONT, BRANCH, FLUSH, TRAP}; + +template +class vm_base: public debugger_if , public vm_if { +public: + typedef typename arch::traits::reg_e reg_e; + typedef typename arch::traits::sreg_flag_e sr_flag_e; + typedef typename arch::traits::virt_addr_t virt_addr_t; + typedef typename arch::traits::phys_addr_t phys_addr_t; + typedef typename arch::traits::addr_t addr_t; + typedef typename arch::traits::code_word_t code_word_t; + typedef typename arch::traits::mem_type_e mem_type_e; + typedef typename arch::traits::addr_t (*func_ptr)(); + + using dbg_if = iss::debugger_if; + + arch_if* get_arch() override {return &core;}; + + constexpr unsigned int get_reg_width(int idx) const { + return idx<0?arch::traits::NUM_REGS:arch::traits::reg_bit_width((reg_e)idx); + } + + template + inline T get_reg(unsigned r){ + std::vector res(sizeof(T),0); + core.get_reg(r, res); + return *reinterpret_cast(&res[0]); + } + + int start(int64_t cycles=-1) override { + int error=0; + if(this->debugging_enabled()) sync_exec=PRE_SYNC; + auto start = std::chrono::high_resolution_clock::now(); + virt_addr_t pc(iss::DEBUG_FETCH, 0, get_reg::addr_t>(arch::traits::PC)); + LOG(INFO)<<"Start at 0x"<second; + } + pc.val = f(); + } catch(trap_access& ta){ + pc.val=core.enter_trap(ta.id, ta.addr); + } +#ifndef NDEBUG + LOG(DEBUG)<<"continuing @0x"<(elapsed).count(); + LOG(INFO)<<"Executed "<::addr_t>(arch::traits::PC); + tgt_adapter->check_continue(pc); + } + } + + virtual void post_instr_sync(){ + core.notify_phase(iss::arch_if::IEND); + } + +protected: + + std::tuple disass(virt_addr_t& pc) { + unsigned blk_size=std::numeric_limits::max(); + unsigned cur_blk=0; + virt_addr_t cur_pc=pc; + processing_pc_entry addr(*this, pc, this->core.v2p(pc)); + unsigned int num_inst = 0; + func = this->open_block_func(); + leave_blk = llvm::BasicBlock::Create(mod->getContext(), "leave", func); + gen_leave_behavior(leave_blk); + trap_blk = llvm::BasicBlock::Create(mod->getContext(), "trap", func); + gen_trap_behavior(trap_blk); + llvm::BasicBlock* bb=llvm::BasicBlock::Create(mod->getContext(), "entry", func, leave_blk); + vm::continuation_e cont=iss::vm::CONT; + try { + while(cont==CONT && cur_blkSetInsertPoint(bb); + std::tie(cont, bb)=gen_single_inst_behavior(cur_pc, num_inst, bb); + cur_blk++; + } + if(bb!=nullptr){ + builder->SetInsertPoint(bb); + builder->CreateBr(leave_blk); + } + const phys_addr_t end_pc(this->core.v2p(--cur_pc)); + assert(this->processing_pc.top().first.val <= cur_pc.val); + return std::make_tuple(cont, func); + } catch(trap_access& ta){ + const phys_addr_t end_pc(this->core.v2p(--cur_pc)); + if(this->processing_pc.top().first.val <= cur_pc.val){ // close the block and return result up to here + builder->SetInsertPoint(bb); + builder->CreateBr(leave_blk); + return std::make_tuple(cont, func); + } else // re-throw if it was the first instruction + throw ta; + } + } + + virtual std::tuple gen_single_inst_behavior(virt_addr_t& pc_v, unsigned int& inst_cnt, llvm::BasicBlock* this_block) = 0; + + virtual void gen_trap_behavior(llvm::BasicBlock*) = 0; + + virtual void gen_leave_behavior(llvm::BasicBlock* leave_blk){ + builder->SetInsertPoint(leave_blk); + llvm::Value * const pc_v = gen_get_reg(arch::traits::NEXT_PC, "next_pc"); + builder->CreateRet(pc_v); + } + + explicit vm_base(ARCH& core, bool dump=false) + : core(core) + , sync_exec(NO_SYNC) // TODO: should be NO_SYNC but this needs to changes code generation + , builder(new llvm::IRBuilder<>(getContext())) + , jitHelper(getContext(), dump) + , mod(nullptr) + , func(nullptr) + , leave_blk(nullptr) + , trap_blk(nullptr) + , tgt_adapter(nullptr) + { + //auto* const_int64_19 = llvm::ConstantInt::get(getContext(), llvm::APInt(64, this, 10)); + core_ptr = llvm::ConstantExpr::getCast(llvm::Instruction::IntToPtr, + gen_const(64, (uintptr_t)static_cast(&core)), // make sure it's the same type as in the dispatch function cause of vtable correction + llvm::PointerType::get(get_type(8), 0)); + vm_ptr = llvm::ConstantExpr::getCast(llvm::Instruction::IntToPtr, + gen_const(64, (uintptr_t)static_cast(this)), + llvm::PointerType::get(get_type(8), 0)); + } + + ~vm_base(){ + delete tgt_adapter; + } + + inline llvm::Type *get_type(unsigned width) const { + assert(width>0); + if (width<2) + return builder->getInt1Ty(); + else if (width<9) + return builder->getInt8Ty(); + else if (width<17) + return builder->getInt16Ty(); + else if(width<33) + return builder->getInt32Ty(); + else if(width<65) + return builder->getInt64Ty(); + assert(!"Not supported yet"); + return builder->getInt64Ty(); + } + + inline llvm::Value* adj_from64(llvm::Value* val, size_t len){ + if (len != 64) + return builder->CreateTrunc(val, get_type(len)); + else + return val; + } + + inline llvm::Value *adj_to64(llvm::Value *val){ + return val->getType()->getScalarSizeInBits() == 64 ? val : builder->CreateZExt(val, builder->getInt64Ty()); + } + + inline std::string gen_var_name(const char* prefix, const char* op, int id){ + std::stringstream ss; + ss << prefix << op << id; + std::string str(ss.str()); + jitHelper.GenerateUniqueName(str, processing_pc.top().second.val); + return str; + } + + inline llvm::Value* gen_get_reg(reg_e r, const char *nm = "") { + std::string str=gen_var_name(nm, "-reg", r); + std::vector args { + core_ptr, + reg_index(r) + }; + return adj_from64(builder->CreateCall(mod->getFunction("get_reg"), args, str.c_str()), arch::traits::reg_bit_width(r)); + } + + inline void gen_set_reg(reg_e r, llvm::Value *val) { + std::vector args { + core_ptr, + reg_index(r), + adj_to64(val) + }; + builder->CreateCall(mod->getFunction("set_reg"), args); + } + + inline llvm::Value* gen_get_flag(sr_flag_e flag, const char *nm = "") { + std::string str= gen_var_name(nm, "_flag", flag); + std::vector args { + core_ptr, + llvm::ConstantInt::get(getContext(), llvm::APInt(16, flag)) + }; + llvm::Value* call = builder->CreateCall(mod->getFunction("get_flag"), args); + return builder->CreateTrunc(call, get_type(1), str.c_str()); + } + + inline void gen_set_flag(sr_flag_e flag, llvm::Value *val) { + std::vector args { + core_ptr, + llvm::ConstantInt::get(getContext(), llvm::APInt(16, flag)), + builder->CreateTrunc(val, get_type(1)) + }; + builder->CreateCall(mod->getFunction("set_flag"), args); + } + + inline void gen_update_flags(iss::arch_if::operations op, llvm::Value *oper1, llvm::Value *oper2) { + std::vector args { + core_ptr, + llvm::ConstantInt::get(getContext(), llvm::APInt(16, op)), + oper1->getType()->getScalarSizeInBits() == 64 ? oper1 : builder->CreateZExt(oper1, llvm::IntegerType::get(mod->getContext(), 64)), + oper2->getType()->getScalarSizeInBits() == 64 ? oper2 : builder->CreateZExt(oper2, llvm::IntegerType::get(mod->getContext(), 64)) + }; + builder->CreateCall(mod->getFunction("update_flags"), args); + } + + inline llvm::Value* gen_read_mem(mem_type_e type, uint64_t addr, uint32_t length, const char *nm = "") { + return gen_read_mem(type, gen_const(64, addr), length, nm); + } + + inline llvm::Value* gen_read_mem(mem_type_e type, llvm::Value* addr, uint32_t length, const char *nm = "") { + auto* storage = builder->CreateAlloca(llvm::IntegerType::get(mod->getContext(), length*8)); + auto* storage_ptr = builder->CreateBitCast(storage, get_type(8)->getPointerTo(0)); + std::string str= gen_var_name(nm, "_mem", type); + std::vector args { + core_ptr, + llvm::ConstantInt::get(getContext(), llvm::APInt(32, iss::VIRTUAL)), + llvm::ConstantInt::get(getContext(), llvm::APInt(32, type)), + adj_to64(addr), + llvm::ConstantInt::get(getContext(), llvm::APInt(32, length)), + storage_ptr + }; + auto* call = builder->CreateCall(mod->getFunction("read_mem"), args); + call->setCallingConv(llvm::CallingConv::C); + auto* icmp = builder->CreateICmpNE(call, gen_const(8, 0UL)); + auto* label_cont = llvm::BasicBlock::Create(getContext(), "", func, this->leave_blk); + llvm::SmallVector Weights(2, UnlikelyBranchWeight); + Weights[1] = LikelyBranchWeight; + this->builder->CreateCondBr(icmp, trap_blk, label_cont, llvm::MDBuilder(this->mod->getContext()).createBranchWeights(Weights)); + builder->SetInsertPoint(label_cont); + switch(length){ + case 1: + case 2: + case 4: + case 8: + return builder->CreateLoad(builder->CreateBitCast(storage, get_type(length*8)->getPointerTo(0)),false); + default: + return storage_ptr; + } + } + + inline void gen_write_mem(mem_type_e type, uint64_t addr, llvm::Value *val){ + gen_write_mem(type, llvm::ConstantInt::get(getContext(), llvm::APInt(64, addr)), val); + } + + inline void gen_write_mem(mem_type_e type, llvm::Value* addr, llvm::Value *val){ + uint32_t bitwidth = val->getType()->getIntegerBitWidth(); + auto* storage = builder->CreateAlloca(llvm::IntegerType::get(mod->getContext(), bitwidth)); + builder->CreateStore(val, storage, false); + auto* storage_ptr = builder->CreateBitCast(storage, get_type(8)->getPointerTo(0)); + std::vector args { + core_ptr, + llvm::ConstantInt::get(getContext(), llvm::APInt(32, iss::VIRTUAL)), + llvm::ConstantInt::get(getContext(), llvm::APInt(32, type)), + adj_to64(addr), + llvm::ConstantInt::get(getContext(), llvm::APInt(32, bitwidth/8)), + storage_ptr + }; + auto* call = builder->CreateCall(mod->getFunction("write_mem"), args); + call->setCallingConv(llvm::CallingConv::C); + auto* icmp = builder->CreateICmpNE(call, gen_const(8, 0UL)); + auto* label_cont = llvm::BasicBlock::Create(getContext(), "", func, this->leave_blk); + llvm::SmallVector Weights(2, UnlikelyBranchWeight); + Weights[1] = LikelyBranchWeight; + this->builder->CreateCondBr(icmp, trap_blk, label_cont, llvm::MDBuilder(this->mod->getContext()).createBranchWeights(Weights)); + builder->SetInsertPoint(label_cont); + } + + inline + llvm::Value* get_reg_ptr(unsigned i, unsigned size){ + void* ptr = this->core.get_regs_base_ptr()+arch::traits::reg_byte_offset(i); + return llvm::ConstantExpr::getIntToPtr( + llvm::ConstantInt::get(this->mod->getContext(), llvm::APInt( + 8/*bits*/ * sizeof(uint8_t*), + reinterpret_cast(ptr) + )), + get_type(size) + ); + } + + template::value>::type* = nullptr> + inline llvm::ConstantInt * gen_const(unsigned size, T val) const{ + return llvm::ConstantInt::get(getContext(), llvm::APInt(size, val, true)); + } + + template::value>::type* = nullptr> + inline llvm::ConstantInt * gen_const(unsigned size, T val) const{ + return llvm::ConstantInt::get(getContext(), llvm::APInt(size, (uint64_t)val, false)); + } + + template::value>::type* = nullptr > + inline llvm::Value* gen_ext(T val, unsigned size) const { + //vm::gen_ext(this->builder, val, size, isSigned); + return llvm::ConstantInt::get(getContext(), llvm::APInt(size, val, false)); + } + + template::value>::type* = nullptr > + inline llvm::Value* gen_ext(T val, unsigned size) const { + //vm::gen_ext(this->builder, val, size, isSigned); + return llvm::ConstantInt::get(getContext(), llvm::APInt(size, val, false)); + } + + template::value, int>::type* = nullptr> + inline llvm::Value* gen_ext(T val, unsigned size) const { + return builder->CreateZExtOrTrunc(val, builder->getIntNTy(size)); + } + + template::value>::type* = nullptr> + inline llvm::Value* gen_ext(T val, unsigned size, bool isSigned) const { + //vm::gen_ext(this->builder, val, size, isSigned); + return llvm::ConstantInt::get(getContext(), llvm::APInt(size, val, isSigned)); + } + + template::value, int>::type* = nullptr> + inline llvm::Value* gen_ext(T val, unsigned size, bool isSigned) const { + if(isSigned) + return builder->CreateSExtOrTrunc(val, builder->getIntNTy(size)); + else + return builder->CreateZExtOrTrunc(val, builder->getIntNTy(size)); + } + + inline llvm::Value* gen_cond_assign(llvm::Value *cond, llvm::Value *t, llvm::Value *f) const { //cond must be 1 or 0 + using namespace llvm; + Value * const f_mask = builder->CreateSub( + builder->CreateZExt(cond, get_type(f->getType()->getPrimitiveSizeInBits())), + gen_const(f->getType()->getPrimitiveSizeInBits(), 1)); + Value * const t_mask = builder->CreateXor(f_mask, gen_const(f_mask->getType()->getScalarSizeInBits(), -1)); + return builder->CreateOr(builder->CreateAnd(t, t_mask), builder->CreateAnd(f, f_mask)); // (t & ~t_mask) | (f & f_mask) + } + + inline void gen_cond_branch(llvm::Value *when, llvm::BasicBlock* then, llvm::BasicBlock* otherwise, unsigned likelyBranch=0) const { //cond must be 1 or 0 + llvm::SmallVector Weights(2, UnlikelyBranchWeight); + if(likelyBranch==1) Weights[0] = LikelyBranchWeight; else if(likelyBranch==2) Weights[1] = LikelyBranchWeight; + this->builder->CreateCondBr(when, then, otherwise, llvm::MDBuilder(this->mod->getContext()).createBranchWeights(Weights)); + } + + inline void gen_sync(sync_type s){ + if((s & sync_exec) == PRE_SYNC) builder->CreateCall(mod->getFunction("pre_instr_sync"), std::vector{vm_ptr}); + if((s & sync_exec) == POST_SYNC) builder->CreateCall(mod->getFunction("post_instr_sync"),std::vector{vm_ptr}); + } + + virtual llvm::Function* open_block_func() { + std::string name("block"); + jitHelper.GenerateUniqueName(name, processing_pc.top().second.val); + std::vector mainFuncTyArgs; + llvm::Type * ret_t = get_type(get_reg_width(arch::traits::PC)); + llvm::FunctionType* const mainFuncTy = llvm::FunctionType::get(ret_t, mainFuncTyArgs, false); + llvm::Function* f = llvm::Function::Create(mainFuncTy, llvm::GlobalValue::ExternalLinkage, name.c_str(), mod.get()); + f->setCallingConv(llvm::CallingConv::C); + return f; + } + + llvm::ConstantInt* reg_index(unsigned r) const { + return llvm::ConstantInt::get(getContext(), llvm::APInt(16, r)); + } + struct processing_pc_entry{ + processing_pc_entry(vm_base& vm, vm_base::virt_addr_t pc_v, vm_base::phys_addr_t pc_p):vm(vm){ + vm.processing_pc.push(std::make_pair(pc_v, pc_p)); + } + ~processing_pc_entry(){ + vm.processing_pc.pop(); + } + protected: + vm_base& vm; + }; + ARCH& core; + sync_type sync_exec; + llvm::Value *core_ptr=nullptr, *vm_ptr=nullptr; + llvm::IRBuilder<> * builder=nullptr; + MCJIT_arch_helper jitHelper; + std::unique_ptr mod; + llvm::Function* func; + llvm::BasicBlock *leave_blk, *trap_blk; + //std::map > > func_map; +// Loki::AssocVector > > func_map; + Loki::AssocVector func_map; + + std::stack > processing_pc; + std::pair regs[arch::traits::NUM_REGS]; //latest_reg, is_dirty + iss::debugger::target_adapter_base* tgt_adapter; +}; + +} +} + +#endif /* _VM_BASE_H_ */ diff --git a/incl/iss/vm_if.h b/incl/iss/vm_if.h new file mode 100644 index 0000000..fe93125 --- /dev/null +++ b/incl/iss/vm_if.h @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _VM_IF_H_ +#define _VM_IF_H_ + +#include "vm_types.h" +#include + +namespace iss { +// forward declaration +struct arch_if; + +struct vm_if { + /** + * get the underlying class of the core to be simulated + * @return pointer to the core + */ + virtual arch_if* get_arch() = 0; + /** + * start the simulation + * @param cycles number if instructions to be simulated + * @return number of executed instructions + */ + virtual int start(int64_t cycles=-1) = 0; + /** + * reset the core + * @param address start address after reset + */ + virtual void reset(uint64_t address) = 0; + /** + * reset the core starting at address 0 + */ + virtual void reset() = 0; + /** + * syncronization point at the beginning of the instruction + */ + virtual void pre_instr_sync()=0; + /** + * syncronization point at the beginning of the instruction + */ + virtual void post_instr_sync()=0; + /** + * check if instruction disassembly is enabled + * @return true if enabled + */ + bool isDisassEnabled(){return disass_enabled;} + /** + * set the disassembly flag + * @param enable the flag to enable disassembling + */ + void setDisassEnabled(bool enable){disass_enabled=enable;} + /** + * destructor + */ + virtual ~vm_if() {} + +protected: + bool disass_enabled; +}; +/** + * exception class signaling an error while decoding an instruction + */ +struct decoding_error: public std::runtime_error { + const uint64_t addr; + decoding_error(uint64_t a): std::runtime_error("decoding error"), addr(a){} +}; +/** + * exception class signaling end of simulation, state contains the result + */ +struct simulation_stopped: public std::runtime_error { + const int state; + simulation_stopped(int s): std::runtime_error("simulation stopped"), state(s){} +}; + +} + +#endif /* _VM_IF_H_ */ diff --git a/incl/iss/vm_types.h b/incl/iss/vm_types.h new file mode 100644 index 0000000..67b16f2 --- /dev/null +++ b/incl/iss/vm_types.h @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _VM_TYPES_H_ +#define _VM_TYPES_H_ + +#include +#include +#include +#include + +namespace iss { + +enum status {Ok, Err, NotSupported }; + +enum sync_type {NO_SYNC=0, PRE_SYNC=1, POST_SYNC=2, ALL_SYNC=3}; + +enum access_type {WRITE=0, READ=1, FETCH=3, DATA=0, CODE=2, DEBUG_WRITE=4, DEBUG_READ=5, DEBUG_FETCH=7, DEBUG=DEBUG_WRITE, ACCESS_TYPE=0x7}; + +enum address_type {LOGICAL=0x10, VIRTUAL=0x20, PHYSICAL=0x30, ADDRESS_TYPE=0x30}; + +struct addr_t { + const unsigned type; + unsigned space; + uint64_t val; + + unsigned getAccessType() const {return type&ACCESS_TYPE;}; + + constexpr addr_t():type(READ|PHYSICAL), space(0), val(0){} + constexpr addr_t(access_type acc_t, address_type addr_t, unsigned s, uint64_t addr):type(acc_t|addr_t), space(s), val(addr){} + constexpr addr_t(unsigned t, unsigned s, uint64_t addr):type(t), space(s), val(addr){} + constexpr addr_t(const addr_t& o):type(o.type), space(o.space), val(o.val){ } + + + constexpr addr_t& operator=(uint64_t o){ + val=o; + return *this; + } + + constexpr addr_t& operator=(const addr_t& o){ + val=o.val; + return *this; + } + + constexpr addr_t operator +(const addr_t& o) const { + assert(type==o.type && space == o.space); + addr_t ret(*this); + ret.val+=o.val; + return ret; + } + + addr_t& operator ++() {//prefix increment + val++; + return *this; + } + + addr_t operator ++ (int unused){ //postfix increment + addr_t ret(*this); + val++; + return ret; + } + + addr_t& operator --() { + val--; + return *this; + } + + constexpr addr_t operator+(int m) const{ + addr_t ret(*this); + ret.val+=m; + return ret; + } + + constexpr addr_t operator-(int m) const{ + addr_t ret(*this); + ret.val-=m; + return ret; + } + +}; + +inline +std::ostream& operator<<(std::ostream& os, const addr_t& op){ + os<<"["< +struct typed_addr_t: public addr_t { + constexpr typed_addr_t():addr_t(READ, TYPE, 0){} + constexpr typed_addr_t(access_type t, uint64_t v):addr_t(t, TYPE, 0, v){} + constexpr typed_addr_t(unsigned t, unsigned s, uint64_t v):addr_t((t&ACCESS_TYPE)|TYPE, s, v){} + constexpr typed_addr_t(const addr_t& o):addr_t(o.getAccessType()|TYPE, o.space, o.val){ } +}; + +} + +namespace iss { +template +class PrimitiveTypeHolder { + typedef PrimitiveTypeHolder this_type; +public: + explicit PrimitiveTypeHolder(T v) : v(v) { + } + operator const T() const { + return v; + } + const this_type operator +(const this_type &other) const { + return this_type(v + other.v); + } + this_type& operator ++() {//prefix increment + v++; + return *this; + } + this_type operator ++ (int unused){ //postfix increment + this_type ret(*this); + v++; + return ret; + } + this_type& operator --() { + v--; + return *this; + } + this_type operator+(int m) const{ + return this_type(v+m); + } + + this_type operator-(int m) const{ + return this_type(v-m); + } + + this_type& operator=(T o){ + v=o; + return *this; + } +private: + T v; +}; +} + +#endif /* _VM_TYPES_H_ */ diff --git a/incl/util/assert.h b/incl/util/assert.h new file mode 100644 index 0000000..1ac38b8 --- /dev/null +++ b/incl/util/assert.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _ASSERT_H_ +#define _ASSERT_H_ + +#include + +#ifndef NDEBUG +# define ASSERT(condition, message) \ + do { \ + if (! (condition)) { \ + LOG(FATAL) << "Assertion `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \ + std::terminate(); \ + } \ + } while (false) +#else +# define ASSERT(condition, message) do { } while (false) +#endif + +#endif /* _ASSERT_H_ */ diff --git a/incl/util/bit_field.h b/incl/util/bit_field.h new file mode 100644 index 0000000..553a63e --- /dev/null +++ b/incl/util/bit_field.h @@ -0,0 +1,201 @@ +/*--------------------------------------------------------- +Copyright (c) 2015 Jeff Preshing + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +---------------------------------------------------------*/ + +#ifndef BIT_FIELD_H_ +#define BIT_FIELD_H_ + + +#ifndef __CPP11OM_BITFIELD_H__ +#define __CPP11OM_BITFIELD_H__ + +#include + +//--------------------------------------------------------- +// BitFieldMember<>: Used internally by ADD_BITFIELD_MEMBER macro. +// All members are public to simplify compliance with sections 9.0.7 and +// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior. +//--------------------------------------------------------- +template +struct BitFieldMember { + T value; + + static_assert(Offset + Bits <= (int) sizeof(T) * 8, "Member exceeds bitfield boundaries"); + static_assert(Bits < (int) sizeof(T) * 8, "Can't fill entire bitfield with one member"); + + static const T Maximum = (T(1) << Bits) - 1; + static const T Mask = Maximum << Offset; + T maximum() const { + return Maximum; + } + T one() const { + return T(1) << Offset; + } + + operator T() const { + return (value >> Offset) & Maximum; + } + + BitFieldMember& operator=(T v) { + assert(v <= Maximum); // v must fit inside the bitfield member + value = (value & ~Mask) | (v << Offset); + return *this; + } + + BitFieldMember& operator+=(T v) { + assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member + value += v << Offset; + return *this; + } + + BitFieldMember& operator-=(T v) { + assert(T(*this) >= v); // result must not underflow + value -= v << Offset; + return *this; + } + + BitFieldMember& operator++() { + return *this += 1; + } + BitFieldMember operator++(int) { // postfix form + BitFieldMember tmp(*this); + operator++(); + return tmp; + } + BitFieldMember& operator--() { + return *this -= 1; + } + BitFieldMember operator--(int){ // postfix form + BitFieldMember tmp(*this); + operator--(); + return tmp; + } +}; + +//--------------------------------------------------------- +// BitFieldArray<>: Used internally by ADD_BITFIELD_ARRAY macro. +// All members are public to simplify compliance with sections 9.0.7 and +// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior. +//--------------------------------------------------------- +template +struct BitFieldArray { + T value; + + static_assert(BaseOffset + BitsPerItem * NumItems <= (int) sizeof(T) * 8, "Array exceeds bitfield boundaries"); + static_assert(BitsPerItem < (int) sizeof(T) * 8, "Can't fill entire bitfield with one array element"); + + static const T Maximum = (T(1) << BitsPerItem) - 1; + T maximum() const { + return Maximum; + } + int numItems() const { + return NumItems; + } + + class Element { + private: + T& value; + int offset; + + public: + Element(T& value, int offset) + : value(value), offset(offset) { + } + T mask() const { + return Maximum << offset; + } + + operator T() const { + return (value >> offset) & Maximum; + } + + Element& operator=(T v) { + assert(v <= Maximum); // v must fit inside the bitfield member + value = (value & ~mask()) | (v << offset); + return *this; + } + + Element& operator+=(T v) { + assert(T(*this) + v <= Maximum); // result must fit inside the bitfield member + value += v << offset; + return *this; + } + + Element& operator-=(T v) { + assert(T(*this) >= v); // result must not underflow + value -= v << offset; + return *this; + } + + Element& operator++() { + return *this += 1; + } + Element operator++(int) { // postfix form + Element tmp(*this); + operator++(); + return tmp; + } + Element& operator--() { + return *this -= 1; + } + Element operator--(int) { // postfix form + Element tmp(*this); + operator--(); + return tmp; + } + }; + + Element operator[](int i) { + assert(i >= 0 && i < NumItems); // array index must be in range + return Element(value, BaseOffset + BitsPerItem * i); + } + + const Element operator[](int i) const { + assert(i >= 0 && i < NumItems); // array index must be in range + return Element(value, BaseOffset + BitsPerItem * i); + } +}; + +//--------------------------------------------------------- +// Bitfield definition macros. +// All members are public to simplify compliance with sections 9.0.7 and +// 9.5.1 of the C++11 standard, thereby avoiding undefined behavior. +//--------------------------------------------------------- +#define BEGIN_BF_DECL(typeName, T) \ + union typeName \ + { \ + struct Wrapper { T value; }; \ + Wrapper wrapper; \ + typeName(T v = 0) { wrapper.value = v; } \ + typeName& operator=(T v) { wrapper.value = v; return *this; } \ + operator T&() { return wrapper.value; } \ + operator T() const { return wrapper.value; } \ + typedef T StorageType; + +#define BF_FIELD(memberName, offset, bits) \ + BitFieldMember memberName; + +#define BF_ARRAY(memberName, offset, bits, numItems) \ + BitFieldArray memberName; + +#define END_BF_DECL() } + +#endif // __CPP11OM_BITFIELD_H__ + +#endif /* BIT_FIELD_H_ */ diff --git a/incl/util/ities.h b/incl/util/ities.h new file mode 100644 index 0000000..e98e899 --- /dev/null +++ b/incl/util/ities.h @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _UTIL_ITIES_H_ +#define _UTIL_ITIES_H_ + +#include +#include + +//some helper functions +template +inline constexpr T bit_sub(T v) { + return (v >> bit) & ((T(1) << width) - 1); +} + +template +inline constexpr typename std::make_signed::type signed_bit_sub(T v) { + typename std::make_signed::type r = v<<(sizeof(T)*8-bit-width); + typename std::make_signed::type ret = (r >> (sizeof(T)*8-width)); + return ret; +} + +// according to http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup +static const int MultiplyDeBruijnBitPosition[32] = + { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; +template +constexpr size_t find_first(std::bitset& bits){ + static_assert(N<=32, "find_first only supports bitsets smaller than 33"); + return MultiplyDeBruijnBitPosition[((uint32_t)((bits.to_ulong() & -bits.to_ulong()) * 0x077CB531U)) >> 27]; +} + +// according to https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation +constexpr size_t bit_count(uint32_t u) { + size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); + return ((uCount + (uCount >> 3)) & 030707070707) % 63; +} + +#endif /* _UTIL_ITIES_H_ */ diff --git a/incl/util/logging.h b/incl/util/logging.h new file mode 100755 index 0000000..116e24c --- /dev/null +++ b/incl/util/logging.h @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEVELS(FOO) FOO(none) FOO(fatal) FOO(error) FOO(warning) FOO(info) FOO(debug) FOO(trace) +#define DO_DESCRIPTION(e) #e, +#define DO_ENUM(e) e, + +namespace logging { + +static const char * const buffer[] = { LEVELS(DO_DESCRIPTION)}; +enum LogLevel { LEVELS(DO_ENUM) }; + +inline std::string NowTime(); + +template +class Log{ +public: + Log(){}; + virtual ~Log(){ + os << std::endl; + T::Output(os.str()); + //TODO: use a more specific exception + if(getLastLogLevel() == fatal) throw std::exception(); + }; + std::ostringstream& Get(LogLevel level = info){ + os << "- " << NowTime(); + os << " " << ToString(level) << ": "; + getLastLogLevel()=level; + return os; + }; +public: + static LogLevel& ReportingLevel(){ + static LogLevel reportingLevel = warning; + return reportingLevel; + } + static std::string ToString(LogLevel level){ + return std::string(getLogLevelCStr()[level]); + }; + static LogLevel FromString(const std::string& level) { + for(unsigned int i=none; i<=trace; i++) + if(!strncasecmp(level.c_str(), (const char*)(getLogLevelCStr()+i), strlen((const char*)getLogLevelCStr()+i))) return i; + Log().Get(warning) << "Unknown logging level '" << level << "'. Using INFO level as default."; + return info; + } + +protected: + LogLevel& getLastLogLevel(){ + static LogLevel level = trace; + return level; + } + static const char* const * getLogLevelCStr(){ + return buffer; + }; + std::ostringstream os; +private: + Log(const Log&); + Log& operator =(const Log&); +}; + +struct Output2FILE { + static FILE*& Stream(){ + static FILE* pStream = stderr; + return pStream; + } + static void Output(const std::string& msg){ + static std::mutex mtx; + std::lock_guard lock(mtx); + FILE* pStream = Stream(); + if (!pStream) return; + fprintf(pStream, "%s", msg.c_str()); + fflush(pStream); + } +}; + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# if defined (BUILDING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllexport) +# elif defined (USING_FILELOG_DLL) +# define FILELOG_DECLSPEC __declspec (dllimport) +# else +# define FILELOG_DECLSPEC +# endif // BUILDING_DBSIMPLE_DLL +#else +# define FILELOG_DECLSPEC +#endif // _WIN32 + +class FILELOG_DECLSPEC Logger : public Log {}; +//typedef Log Logger; + +#ifndef FILELOG_MAX_LEVEL +#define FILELOG_MAX_LEVEL logging::trace +#endif + +#define LOG(level) \ + if (level > FILELOG_MAX_LEVEL) ;\ + else if (level > logging::Logger::ReportingLevel() || !logging::Output2FILE::Stream()) ; \ + else logging::Logger().Get(level) + +#if defined(WIN32) + +#include + +inline std::string NowTime(){ + const int MAX_LEN = 200; + char buffer[MAX_LEN]; + if (GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0, "HH':'mm':'ss", buffer, MAX_LEN) == 0) + return "Error in NowTime()"; + char result[100] = {0}; + static DWORD first = GetTickCount(); + std::sprintf(result, "%s.%03ld", buffer, (long)(GetTickCount() - first) % 1000); + return result; +} + +#else + +inline std::string NowTime(){ + char buffer[11]; + time_t t; + time(&t); + tm r = {0}; + strftime(buffer, sizeof(buffer), "%X", localtime_r(&t, &r)); + struct timeval tv; + gettimeofday(&tv, 0); + char result[100] = {0}; + sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000); + return result; +} + +#endif //WIN32 +// a print function for a vector +template +std::ostream& operator<< (std::ostream& stream, const std::vector& vector) { + copy(vector.begin(), vector.end(), std::ostream_iterator(stream, ",")); + return stream; +} + +} // namespace +#undef LEVELS +#undef CAT + +#ifndef NDEBUG +# define ASSERT(condition, message) \ + do { \ + if (! (condition)) { \ + logging::Logger().Get(logging::fatal) << "Assertion `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \ + std::terminate(); \ + } \ + } while (false) +#else +# define ASSERT(condition, message) do { } while (false) +#endif + +#define CHECK(condition, message) \ +do { \ + if (! (condition)) { \ + logging::Logger().Get(logging::fatal) << "Check of `" #condition "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \ + std::terminate(); \ + } \ +} while (false) + +#endif /* _LOGGING_H_ */ diff --git a/incl/util/range_lut.h b/incl/util/range_lut.h new file mode 100644 index 0000000..12f6af1 --- /dev/null +++ b/incl/util/range_lut.h @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _RANGE_LUT_H_ +#define _RANGE_LUT_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace util { + +template +class range_lut{ + enum entry_type {BEGIN_RANGE=1, END_RANGE=2, SINGLE_BYTE_RANGE=3}; + + struct lut_entry { + T index; + entry_type type; + }; + +public: + /** + * constructor or the lookup table + * @param null_entry the entry to be used for empty slots + */ + range_lut(T null_entry):null_entry(null_entry){ } + /** + * add an T to the lut covering the range starting at base_addr until base_addr+size-1 + * @param i the entry + * @param base_addr the base address + * @param size the size of the occupied range + */ + void addEntry(T i, uint64_t base_addr, uint64_t size); + /** + * remove an entry with value i of type T + * @param i the entry to be found + * @return true if the entry is found and removed, false otherwise + */ + bool removeEntry(T i); + /** + * get number of entries in the lookup table + * @return the size of the underlying container + */ + size_t size(){ + return lut.size(); + } + /** + * remove all entries from the lut + */ + void clear(){ + lut.clear(); + } + /** + * get the entry T associated with a given address + * @param addr the address + * @return the entry belonging to the address + */ + inline + T getEntry(uint64_t addr){ + auto iter = lut.lower_bound(addr); + return (iter!=lut.end() && (iter->second.type==END_RANGE ||iter->first==addr))? iter->second.index:null_entry; + } + /** + * validate the lookup table wrt. overlaps + */ + void validate() ; + /** + * create a textual representation of the address map (address range->entry association) + * @return + */ + std::string toString(); + /** + * the null entry + */ + const T null_entry; +protected: + Loki::AssocVector lut; +}; + +/** + * overloaded stream operator + * @param os the output stream + * @return the stream + */ +template +std::ostream& operator <<(std::ostream& os, range_lut& lut) { + os << lut.toString(); + return os; +} + +template +inline void range_lut::addEntry(T i, uint64_t base_addr, uint64_t size) { + auto iter = lut.find(base_addr); + if (iter != lut.end() && iter->second.index != null_entry) throw std::runtime_error("range already mapped"); + + uint64_t eaddr = base_addr + size - 1; + if (eaddr < base_addr) throw std::runtime_error("address wrap-around occurred"); + + lut[base_addr] = lut_entry { i, size > 1 ? BEGIN_RANGE : SINGLE_BYTE_RANGE }; + if (size > 1) lut[eaddr] = lut_entry { i, END_RANGE }; +} + +template +inline bool range_lut::removeEntry(T i) { + throw std::exception(); +// auto iter = rlut.find(i); +// if (iter != rlut.end()) { +// uint64_t baddr = iter->second; +// auto beg_iter = lut.find(baddr); +// if (beg_iter->second.type == SINGLE_BYTE_RANGE) { +// lut.erase(beg_iter); +// } else { +// auto end_iter = beg_iter; +// ++end_iter; +// lut.erase(beg_iter, ++end_iter); +// } +// rlut.erase(iter); +// return true; +// } + return false; +} + +template +inline void range_lut::validate() { + bool mapped = false; + for (auto iter = lut.begin(); iter != lut.end(); iter++) { + switch (iter->second.type) { + case SINGLE_BYTE_RANGE: + if (iter->second.index != null_entry && mapped) throw std::runtime_error("range overlap: begin range while in mapped range"); + + break; + case BEGIN_RANGE: + if (iter->second.index != null_entry) { + if (mapped) { + throw std::runtime_error("range overlap: begin range while in mapped range"); + } + mapped = true; + } + break; + case END_RANGE: + if (!mapped) { + throw std::runtime_error("range overlap: end range while in unmapped region"); + } + mapped = false; + break; + } + } +} + +template +inline std::__cxx11::string range_lut::toString() { + std::ostringstream buf; + for (auto iter = lut.begin(); iter != lut.end(); ++iter) { + switch (iter->second.type) { + case BEGIN_RANGE: + if (iter->second.index != null_entry) { + buf << " from 0x" << std::setw(sizeof(uint64_t) * 2) << std::setfill('0') << std::uppercase << std::hex << iter->first + << std::dec; + } + break; + case END_RANGE: + buf << " to 0x" << std::setw(sizeof(uint64_t) * 2) << std::setfill('0') << std::uppercase << std::hex << iter->first << std::dec + << " as " << iter->second->index << std::endl; + } + } + return buf.str(); +} + + +} // namespace util + + +#endif /* _RANGE_LUT_H_ */ diff --git a/incl/util/sparse_array.h b/incl/util/sparse_array.h new file mode 100644 index 0000000..e9b7a95 --- /dev/null +++ b/incl/util/sparse_array.h @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ + +#ifndef _SPARSE_ARRAY_H_ +#define _SPARSE_ARRAY_H_ + +#include +#include + +namespace util { + +/** + * a simple array which allocates memory in 16MB chunks + */ +template +struct sparse_array { + + const uint64_t page_addr_mask = (1<; + + + sparse_array(){ + arr.fill(nullptr); + } + + ~sparse_array(){ + for(auto i:arr) delete i; + } + + T& operator[](uint32_t addr) { + assert(addr>lower_width; + if(arr[nr] == nullptr) arr[nr]= new page_type(); + return arr[nr]->at(addr & page_addr_mask); + } + + page_type& operator()(uint32_t page_nr) { + assert(page_nr<(SIZE/(1< arr; +}; + +} + +#endif /* _SPARSE_ARRAY_H_ */ diff --git a/incl/util/thread_syncronizer.h b/incl/util/thread_syncronizer.h new file mode 100755 index 0000000..756f8b7 --- /dev/null +++ b/incl/util/thread_syncronizer.h @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (C) 2017, MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Contributors: + * eyck@minres.com - initial API and implementation + ******************************************************************************/ +#ifndef _THREAD_SYNCRONIZER_H_ +#define _THREAD_SYNCRONIZER_H_ + + +#include +#include +#include +#include +#include + +class thread_syncronizer { +private: + std::queue< std::function< void() > > tasks_; + std::atomic ready; + std::mutex mutex_; + std::condition_variable condition_; +public: + /// @brief Constructor. + thread_syncronizer() { } + + /// @brief Destructor. + ~thread_syncronizer(){ + // Set running flag to false then notify all threads. + condition_.notify_all(); + } + + bool is_ready(){return ready.load(std::memory_order_acquire);} + + template + typename std::result_of::type enqueue_and_wait(F&& f, Args&&... args) { + auto res = enqueue(f, args...); + res.wait(); + return res.get(); + } + + + template + auto enqueue(F&& f, Args&&... args) -> std::future::type> { + using return_type = typename std::result_of::type; + auto task = std::make_shared< std::packaged_task >( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(mutex_); + tasks_.emplace([task](){ (*task)(); }); + } + condition_.notify_one(); + return res; + } + /// @brief execute the next task in queue but do not wait for the next one + void execute(){ + if(tasks_.empty()) return ; + { + std::unique_lock< std::mutex > lock( mutex_ ); + // Copy task locally and remove from the queue. This is done within + // its own scope so that the task object is destructed immediately + // after running the task. This is useful in the event that the + // function contains shared_ptr arguments bound via bind. + std::function< void() > functor = tasks_.front(); + tasks_.pop(); + lock.unlock(); + // Run the task. + try{ + functor(); + } catch ( ... ) {} // Suppress all exceptions. + } + } + + /// @brief execute the next task in queue or wait for the next one + void executeNext(){ + ready.store(true, std::memory_order_release); + // Wait on condition variable while the task is empty + std::unique_lock< std::mutex > lock( mutex_ ); + while ( tasks_.empty() && ready.load(std::memory_order_acquire)){ + condition_.wait_for(lock, std::chrono::milliseconds(10)); + } + lock.unlock(); + execute(); + ready.store(false, std::memory_order_release); + } +}; +#endif /* _THREAD_SYNCRONIZER_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..b7a5686 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,41 @@ +FILE(GLOB IssHeaders *.h) +set(LIB_HEADERS ${IssHeaders} ) +set(LIB_SOURCES + dbgsrvbase.cpp + gdb_session.cpp + MCJIThelper.cpp + vm_base.cpp + cmdhandler.cpp + target_adapter_base.cpp + encoderdecoder.cpp +) + +# Define two variables in order not to repeat ourselves. +set(LIBRARY_NAME iss) + +# Define the library +add_library(${LIBRARY_NAME} SHARED ${LIB_SOURCES}) +#add_library(${LIBRARY_NAME} ${LIB_SOURCES}) +SET(${LIBRARY_NAME} -Wl,-whole-archive -l${LIBRARY_NAME} -Wl,-no-whole-archive) +# Set the build version. It will be used in the name of the lib, with corresponding +# symlinks created. SOVERSION could also be specified for api version. +set_target_properties(${LIBRARY_NAME} PROPERTIES + VERSION ${VERSION} + FRAMEWORK FALSE + PUBLIC_HEADER "${LIB_HEADERS}" +) + +# Says how and where to install software +# Targets: +# * /lib/ +# * header location after install: /include//*.h +# * headers can be included by C++ code `#/Bar.hpp>` +install(TARGETS ${LIBRARY_NAME} + EXPORT ${PROJECT_NAME}Targets # for downstream dependencies + ARCHIVE DESTINATION lib COMPONENT libs # static lib + LIBRARY DESTINATION lib COMPONENT libs # shared lib + FRAMEWORK DESTINATION bin COMPONENT libs # for mac + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} COMPONENT devel # headers for mac (note the different component -> different package) + INCLUDES DESTINATION incl # headers +) + diff --git a/src/MCJIThelper.cpp b/src/MCJIThelper.cpp new file mode 100644 index 0000000..2ef4381 --- /dev/null +++ b/src/MCJIThelper.cpp @@ -0,0 +1,277 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include //EnableDebugBuffering +#include //outs() +#include +#include +#include // llvm::sys::PrintStackTraceOnErrorSignal() +// needed to get the execution engine linked in +#include +#include "llvm/ExecutionEngine/SectionMemoryManager.h" + +#include + +using namespace llvm; +using namespace iss::vm; + +void iss::init_jit(int argc, char *argv[]){ + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + //llvm::PrettyStackTraceProgram X(argc, argv); + //llvm::EnableDebugBuffering = true; +} + +LLVMContext& iss::getContext() { + static LLVMContext context; + return context; +} + +MCJIT_helper::MCJIT_helper(LLVMContext& C, bool dump) + : context(C), dumpEnabled(dump) { +} + +MCJIT_helper::~MCJIT_helper() { +} + +std::string MCJIT_helper::GenerateUniqueName(const char *root) const{ + static int i = 0; + char s[20]; + sprintf(s, "%s#%X_", root, i++); + return std::string(s); +} + +void MCJIT_helper::GenerateUniqueName(std::string &str, uint64_t mod) const { + char buf[21]; + ::snprintf(buf, sizeof(buf), "@0x%016lX_", mod); + str += buf; +} + +std::unique_ptr MCJIT_helper::createModule() { + // create a new Module. + auto mod_name = GenerateUniqueName("mcjit_module_"); + auto mod = std::make_unique(mod_name, context); + add_functions_2_module(mod.get()); + return mod; +} + +ExecutionEngine *MCJIT_helper::compileModule(std::unique_ptr M) { + assert(engineMap.find(M->getModuleIdentifier()) == engineMap.end()); + if(dumpEnabled){ + std::error_code EC; + std::string name(((std::string)M->getName())+".il"); + llvm::raw_fd_ostream OS(llvm::StringRef(name), EC, llvm::sys::fs::F_None); + // llvm::WriteBitcodeToFile(M, OS); + // M->dump(); + M->print(OS, nullptr, false, true); + OS.flush(); + } + + const std::string moduleID = M->getModuleIdentifier(); + std::string ErrStr; + EngineBuilder EEB(std::move(M)); + EEB.setUseOrcMCJITReplacement(true); + ExecutionEngine *EE = EEB + .setErrorStr(&ErrStr) + .setMCJITMemoryManager(std::make_unique()) + .setOptLevel(CodeGenOpt::Aggressive) + .create(); + if (!EE) throw std::runtime_error(ErrStr.c_str()); + EE->finalizeObject(); + // Store this engine + engineMap[moduleID] = EE; + return EE; +} + +void *MCJIT_helper::getPointerToFunction(std::unique_ptr M, const std::string &Name) { + // Look for the functions in our modules, compiling only as necessary + Function *F = M->getFunction(Name); + if (F && !F->empty()) { + ExecutionEngine *EE = compileModule(std::move(M)); + return EE->getPointerToFunction(F); + } + return NULL; +} + +#define INT_TYPE(L) IntegerType::get(mod->getContext(), L) +#define VOID_TYPE Type::getVoidTy(mod->getContext()) +#define THIS_PTR_TYPE INT_TYPE(8)->getPointerTo() +#define FDECLL(NAME,RET, ...) \ +Function* NAME ## _func = CurrentModule->getFunction(#NAME);\ +if (!NAME ## _func) {\ + std::vector NAME ## _args {__VA_ARGS__};\ + FunctionType* NAME ## _type = FunctionType::get(RET, NAME ## _args, false);\ + NAME ## _func = Function::Create(NAME ## _type, GlobalValue::ExternalLinkage, #NAME, CurrentModule);\ + NAME ## _func ->setCallingConv(CallingConv::C);\ +} + +#define FDECL(NAME,RET, ...) \ + std::vector NAME ## _args {__VA_ARGS__};\ + FunctionType* NAME ## _type = llvm::FunctionType::get(RET, NAME ## _args, false);\ + mod->getOrInsertFunction(#NAME, NAME ## _type); + + using namespace llvm; +void MCJIT_helper::add_functions_2_module(Module* mod){ + //Type* voidType = Type::getVoidTy(CurrentModule->getContext()); + FDECL(get_reg, INT_TYPE(64), THIS_PTR_TYPE, INT_TYPE(16)); + FDECL(set_reg, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(16), INT_TYPE(64)); + FDECL(get_flag, INT_TYPE(1), THIS_PTR_TYPE, INT_TYPE(16)); + FDECL(set_flag, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(16), INT_TYPE(1)); + FDECL(update_flags, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(16), INT_TYPE(64), INT_TYPE(64)); + FDECL(fetch, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(fetch_dbg, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(read_mem, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(write_mem, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(read_mem_dbg, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(write_mem_dbg, INT_TYPE(8), THIS_PTR_TYPE, INT_TYPE(32), INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8)->getPointerTo()); + FDECL(enter_trap, INT_TYPE(64), THIS_PTR_TYPE, INT_TYPE(64), INT_TYPE(64)); + FDECL(leave_trap, INT_TYPE(64), THIS_PTR_TYPE, INT_TYPE(64)); + FDECL(wait, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(64)); + FDECL(print_string, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(8)->getPointerTo()); + FDECL(print_disass, VOID_TYPE, THIS_PTR_TYPE, INT_TYPE(8)->getPointerTo()); + FDECL(pre_instr_sync, VOID_TYPE, THIS_PTR_TYPE); + FDECL(post_instr_sync, VOID_TYPE, THIS_PTR_TYPE); +} + +#include +#include +#include + +typedef uint8_t* this_t; +// Use default logger +static el::Logger* get_logger(){ + static el::Logger* logger = el::Loggers::getLogger("disass", true); + return logger; +} + +extern "C" { +uint64_t get_reg(this_t iface, int16_t idx) { + std::vectordata(8, 0); + ((iss::arch_if*)iface)->get_reg(idx, data); +#ifdef EXEC_LOGGING + LOG(TRACE)<<"EXEC: read reg "<(&data[0])); +} + +void set_reg(this_t iface, int16_t idx, uint64_t value) { +#ifdef EXEC_LOGGING + LOG(TRACE)<<"EXEC: write reg "<data(8, 0); + *(reinterpret_cast(&data[0]))=value; + ((iss::arch_if*)iface)->set_reg(idx, data); +} + +bool get_flag(this_t iface, int16_t flag) { + bool res = ((iss::arch_if*)iface)->get_flag(flag); +#ifdef EXEC_LOGGING + LOG(TRACE)<<"EXEC: read flag "<set_flag(flag, value); +} + +void update_flags(this_t iface, int16_t op, uint64_t opr1, uint64_t opr2){ + ((iss::arch_if*)iface)->update_flags((iss::arch_if::operations)op, opr1, opr2); +} + +uint8_t fetch(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ + return ((iss::arch_if*)iface)->read(iss::addr_t{iss::FETCH|addr_type, space, addr}, length, data); +} + +uint8_t fetch_dbg(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ + return ((iss::arch_if*)iface)->read(iss::addr_t{iss::DEBUG_FETCH|addr_type, space, addr}, length, data); +} +uint8_t read_mem(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ + return ((iss::arch_if*)iface)->read(iss::addr_t{iss::READ|addr_type, space, addr}, length, data); +} + +uint8_t write_mem(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ +#ifdef EXEC_LOGGING + LOG(TRACE)<<"EXEC: write mem "<<(unsigned)type<<" of core "<write(iss::addr_t{iss::WRITE|addr_type, space, addr}, length, data); +} + +uint8_t read_mem_dbg(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ + return ((iss::arch_if*)iface)->read(iss::addr_t{iss::DEBUG_READ|addr_type, space, addr}, length, data); +} + +uint8_t write_mem_dbg(this_t iface, uint32_t addr_type, uint32_t space, uint64_t addr, uint32_t length, uint8_t* data){ + return ((iss::arch_if*)iface)->write(iss::addr_t{iss::DEBUG_WRITE|addr_type, space, addr}, length, data); +} + +uint64_t enter_trap(this_t iface, uint64_t flags, uint64_t addr){ + return ((iss::arch_if*)iface)->enter_trap(flags, addr); +} + +uint64_t leave_trap(this_t iface, uint64_t flags){ + return ((iss::arch_if*)iface)->leave_trap(flags); +} + +void wait(this_t iface, uint64_t flags){ + ((iss::arch_if*)iface)->wait_until(flags); +} + +void print_string(this_t iface, char* str){ + LOG(DEBUG)<<"[EXEC] "<info(str, ((iss::arch_if*)iface)->get_additional_disass_info()); +} + +void pre_instr_sync(this_t iface){ + iss::vm_if* vm = reinterpret_cast(iface); + vm->pre_instr_sync(); +} + +void post_instr_sync(this_t iface){ + ((iss::vm_if*)iface)->post_instr_sync(); +} +} + + diff --git a/src/cmdhandler.cpp b/src/cmdhandler.cpp new file mode 100644 index 0000000..40884d0 --- /dev/null +++ b/src/cmdhandler.cpp @@ -0,0 +1,849 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include + +using namespace iss::debugger; + +void rp_console_output(const char *buf){ + CLOG(INFO, "connection")<set_ctrl_thread(ref)); + case 'g': + in = in_buf.c_str()+2; + if (!encdec.dec_uint64(&in, &ref.val, '\0')) + return "E00"; + return to_string(t->set_gen_thread(ref)); + default: + CLOG(ERROR, "connection")<<__FUNCTION__<<": Bad H command"; + return ""; + } +} + +std::string cmd_handler::read_registers(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + size_t len; + std::vector data_buf(MAX_DATABYTES); + std::vector avail_buf(MAX_DATABYTES); + /* Get all registers. Format: 'g'. Note we do not do any data caching - all caching is done by the debugger */ + ret = t->read_registers(data_buf, avail_buf); + if(!ret) { + return encdec.enc_regs(data_buf, avail_buf); + } else + return "E00"; +} + +std::string cmd_handler::write_registers(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + size_t len; + + /* Write all registers. Format: 'GXXXXXXXXXX' */ + std::vector data = encdec.dec_data(&in_buf[1]); + if (data.empty()) + return "E00"; + else + return to_string(t->write_registers(data)); +} + +std::string cmd_handler::read_single_register(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + unsigned int reg_no; +// uint64_t avail=std::numeric_limits::max(); + + /* Get a single register. Format 'pNN' */ + int ret = encdec.dec_reg(&in_buf[1], ®_no); + if (!ret) + return "E00"; + std::vector data(8), avail(8); + ret = t->read_single_register(reg_no, data, avail); + switch (ret) { + case iss::Ok: + assert(data.size() <= MAX_DATABYTES); + return encdec.enc_regs(data, avail); + case iss::Err: + return "E00"; + // handle targets non supporting single register read + case iss::NotSupported: + break; + default: + /* This should not happen */ + assert(0); + break; + } + return ""; +} + +std::string cmd_handler::write_single_register(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + unsigned int reg_no; + /* Write a single register. Format: 'PNN=XXXXX' */ + std::vector data = encdec.dec_reg_assignment(&in_buf[1], ®_no); + if (data.empty()) + return "E00"; + assert(data.size() < MAX_DATABYTES); + return to_string(t->write_single_register(reg_no, data)); +} + +std::string cmd_handler::read_memory(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + size_t len; + uint64_t addr; + + /* Read memory format: 'mAA..A,LL..LL' */ + if (!(ret = encdec.dec_mem(&in_buf[1], &addr, &len))) + return "E00"; + + /* Limit it so buggy gdbs will not complain */ + if (len > ((DBG_PRINTBUF_SIZE - 32) / 2)) + len = (DBG_PRINTBUF_SIZE - 32) / 2; + + std::vector data(len); + ret = t->read_mem(addr, data); + + switch (ret) { + case iss::Ok: + assert(len <= MAX_DATABYTES); + return encdec.enc_data(data); + case iss::Err: + return "E00"; + default: + /* This should not happen */ + assert(0); + break; + } + return ""; +} + +std::string cmd_handler::write_memory(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + size_t cp; + /* Write memory format: 'mAA..A,LL..LL:XX..XX' */ + if ((cp = in_buf.find_first_of(':', 1)) < in_buf.npos) + return "E00"; + + size_t len; + uint64_t addr; + int ret = encdec.dec_mem(&in_buf[1], &addr, &len); + if (!ret || len > MAX_DATABYTES) + return "E00"; + + size_t len1; + std::vector data = encdec.dec_data(&in_buf[cp + 1]); + if (!ret || len != len1) + return "E00"; + return to_string(t->write_mem(addr, data)); +} + +std::string cmd_handler::running(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + bool step; + std::string status_string; + uint32_t sig; + bool running; + bool implemented; + const char *addr_ptr; + uint64_t addr; + int ret; + const char *in; + + /* 's' step from address + * 'S' step from address with signal + * 'c' continue from address + * 'C' continue from address with signal + */ + + step = (in_buf[0] == 'S' || in_buf[0] == 's'); + + addr_ptr = nullptr; + + if (in_buf[0] == 'C' || in_buf[0] == 'S' || in_buf[0] == 'W') { + /* + * Resume with signal. + * Format Csig[;AA..AA], Ssig[;AA..AA], or Wsig[;AA..AA] + */ + + in = &in_buf[1]; + if (strchr(in, ';')) { + if (!encdec.dec_uint32(&in, &sig, ';')) return "E00"; + addr_ptr = in; + } else { + if (!encdec.dec_uint32(&in, &sig, '\0')) return "E00"; + } + } else { + sig = TARGET_SIGNAL0; + if (in_buf[1] != '\0') + addr_ptr = &in_buf[1]; + } + + if (addr_ptr) { + if (!encdec.dec_uint64(&addr_ptr, &addr, '\0')) return "E00"; + ret = t->resume_from_addr(step, sig, addr); + } else { + ret = t->resume_from_current(step, sig); + } + + if(ret==iss::Ok) //answer with SIGTRAP + status_string="T05";// step?"T05":"T06"; + + if (ret != iss::Ok) { + return to_string(ret); + } + + /* Now we have to wait for the target */ + /* Try a non-blocking wait first */ + ret = t->wait_non_blocking(status_string, rp_console_output, running); + if (ret == iss::Err) return to_string(ret); + ret=iss::NotSupported; //TODO: Fix, needs to send back stop if finished + if (ret==iss::NotSupported && !step) { + /* There is no partial wait facility for this target, so use as blocking wait */ + ret = t->wait_blocking(status_string, rp_console_output); + assert(ret!=iss::NotSupported); + return ret == iss::Ok?status_string:to_string(ret); + } + if (!running) { + /* We are done. The program has already stopped */ + return status_string; + } + return ""; +} + +int cmd_handler::kill(const std::string in_buf, std::string& out_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + + s.shutdown(); + + if (!extended_protocol) { + // dbg_sock_close(); + // + // if (!can_restart) { + // /* If the current target cannot restart, we have little choice but to exit right now. */ + // CLOG(INFO, "connection")<<__FUNCTION__<<": session killed. Exiting"; + // dbg_sock_cleanup(); + // exit(0); + // } + + CLOG(INFO, "connection")<<__FUNCTION__<<": session killed. Will wait for a new connection"; + return 0; + } + + CLOG(INFO, "connection")<<__FUNCTION__<<": remote proxy restarting"; + + /* Let us do our best while starting system */ + if (!can_restart) { + /* Even if restart is not supported it is still worth calling connect */ + return -1; + } + + ret = s.reset(CORE_ID); + + assert(ret != iss::NotSupported); + + if (ret != iss::Ok) { + /* There is no point in continuing */ + CLOG(ERROR, "connection")<<__FUNCTION__<<": unable to restart target"; + out_buf="E00"; + // rp_putpkt(out_buf); + // dbg_sock_close(); + // + // if (!can_restart) { + // dbg_sock_cleanup(); + // exit(1); + // } + + CLOG(INFO, "connection")<<__FUNCTION__<<": will wait for a new connection"; + return 0; + } + return 1; +} + +std::string cmd_handler::thread_alive(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + bool alive; + rp_thread_ref ref; + const char *in; + + /* Is thread alive? */ + /* This is a deprecated feature of the remote debug protocol */ + in = &in_buf[1]; + if (!(ret = encdec.dec_uint64(&in, &ref.val, '\0'))) + return "E00"; + + ret = t->is_thread_alive(ref, alive); + if (ret != iss::Ok) { + return to_string(ret); + } else { + if (alive) + return "OK"; + else + return "E00"; + } + return "E01"; +} + +int cmd_handler::restart_target(const std::string in_buf, std::string& out_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + + /* Restarting the target is only supported in the extended protocol. */ + if (!extended_protocol) + return 0; + + assert (can_restart); + + /* Let us do our best to restart the system */ + if ((ret = s.reset(CORE_ID)) != iss::Ok) { + /* There is no point to continuing */ + CLOG(ERROR, "connection")<<__FUNCTION__<<": unable to restart target"; + out_buf="E00"; + CLOG(INFO, "connection")<<__FUNCTION__<<": will wait for a new connection"; + return -1; + } + return 1; +} + +std::string cmd_handler::detach(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + + s.shutdown(); + + // /* Note: The current GDB does not expect a reply */ + // rp_putpkt(out_buf); + // dbg_sock_close(); + + CLOG(INFO, "connection")<<__FUNCTION__<<": debugger detaching"; + + if (!can_restart) { + /* If the current target cannot restart, we have little choice but + to exit right now. */ + CLOG(INFO, "connection")<<__FUNCTION__<<": target is not restartable. Exiting"; + exit(0); + } + + CLOG(INFO, "connection")<<__FUNCTION__<<": will wait for a new connection"; + return ""; +} + +std::string cmd_handler::query(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int ret; + rp_thread_ref ref; + rp_thread_info info; + unsigned int len; + uint32_t val; + uint64_t addr; + + if (in_buf.size() == 1) { + CLOG(ERROR, "connection")<<__FUNCTION__<<": bad 'q' command received"; + return ""; + } + if (strncmp(in_buf.c_str() + 1, "Attached", 8) == 0) { + return "1"; // always attached + } + if (strncmp(in_buf.c_str() + 1, "Offsets", 7) == 0) { + uint64_t text; + uint64_t data; + uint64_t bss; + + /* Get the program segment offsets */ + ret = t->offsets_query(text, data, bss); + if (ret == iss::Ok) { + std::stringstream ss; + ss<crc_query(addr, len, val); + if (ret == iss::Ok){ + std::stringstream ss; + ss<<"C"<threadextrainfo_query(ref, data_buf); + switch (ret) { + case iss::Ok: + return encdec.enc_data((unsigned char *) data_buf.c_str(), strlen(data_buf.c_str())); + break; + case iss::Err: + case iss::NotSupported: + return to_string(ret); + break; + default: + assert(0); + break; + } + return ""; + } + + if (strncmp(in_buf.c_str() + 1, "fThreadInfo", 11) == 0) { + std::string buf; + ret = t->threadinfo_query(1, buf); + switch (ret) { + case iss::Ok: + return buf; + case iss::NotSupported: + case iss::Err: + return to_string(ret); + default: + /* This should not happen */ + assert(0); + } + return ""; + } + + if (strncmp(in_buf.c_str() + 1, "sThreadInfo", 11) == 0) { + std::string buf; + ret = t->threadinfo_query(0, buf); + switch (ret) { + case iss::Ok: + return buf; + case iss::NotSupported: + case iss::Err: + return to_string(ret); + default: + /* This should not happen */ + assert(0); + } + return ""; + } + + if (strncmp(in_buf.c_str() + 1, "fProcessInfo", 12) == 0) { + /* Get first string of process info */ + return ""; + } + + if (strncmp(in_buf.c_str() + 1, "sProcessInfo", 12) == 0) { + /* Get subsequent string of process info */ + return ""; + } + + if (strncmp(in_buf.c_str() + 1, "Rcmd,", 5) == 0) { + /* Remote command */ + return to_string(rcmd(in_buf.c_str()+6, rp_console_output, rp_data_output)); + } + + if (strncmp(in_buf.c_str() + 1, "Supported", 9) == 0 && (in_buf[10] == ':' || in_buf[10] == '\0')) { +// std::string stdFeat("vContSupported+;hwbreak+;swbreak+"); + std::string stdFeat("hwbreak+;swbreak+"); + std::string buf; + ret = t->packetsize_query(buf); + switch (ret) { + case iss::Ok: + return stdFeat+";"+buf; + case iss::NotSupported: + case iss::Err: + return to_string(ret); + default: + /* This should not happen */ + assert(0); + } + return ""; + } + + switch (in_buf[1]) { + case 'C': + /* Current thread query */ + ret = t->current_thread_query(ref); + if (ret == iss::Ok){ + std::stringstream ss; + ss<<"QC"< 255) { + return "E00"; + } + std::vector threads(max_found); + + ret = t->thread_list_query(first, arg, threads, max_found, count, done); + if (ret != iss::Ok || count > max_found) { + return to_string(ret); + } + + std::string res= encdec.enc_list_query_response(count, done, arg, threads); + if (res.size()) + return res; + else + return "E00"; + } + break; + case 'P': + /* Thread info query */ + { + unsigned int mask; + if (!(ret = encdec.dec_process_query(&in_buf[2], &mask, &ref))) + return "E00"; + + info.thread_id.val = 0; + info.display[0] = 0; + info.thread_name[0] = 0; + info.more_display[0] = 0; + + if ((ret = t->process_query(mask, ref, info)) != iss::Ok) + return to_string(ret); + + std::string ret = encdec.enc_process_query_response(mask, &ref, &info); + if (ret.size()) + return ret; + else + return "E00"; + break; + } + default: + /* Raw Query is a universal fallback */ + std::string buf; + ret = t->raw_query(in_buf, buf); + if (ret != iss::Ok) + return to_string(ret); + break; + } + return ""; +} + +std::string cmd_handler::set(const std::string in_buf) { + if (in_buf.find("QPassSignals:", 0) == 0) { + /* Passing signals not supported */ + return(""); + } else if (in_buf.find("QTDP", 0) == 0 || + in_buf.find("QFrame", 0) == 0 || + in_buf.find("QTStart", 0) == 0 || + in_buf.find("QTStop", 0) == 0 || + in_buf.find("QTinit", 0) == 0 || + in_buf.find("QTro", 0) == 0) { + // a placeholder for tracepont packets, empty response to QTDP should disable it */ + return(""); + } + return ""; +} + +std::string cmd_handler::breakpoint(const std::string in_buf) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + uint64_t addr; + unsigned int len; + int type; + int ret; + + if (!(ret = encdec.dec_break(in_buf.c_str(), &type, &addr, &len))) + return "E00"; + + if (in_buf[0] == 'Z'){ + ret = t->add_break(type, addr, len); + return "OK"; + } else{ + try { + ret = t->remove_break(type, addr, len); + return "OK"; + } catch(...){ + return "E00"; + } + } +} + +void cmd_handler::interrupt_target() { + t->stop(); +} + +/* Help function, generate help text from command table */ +int cmd_handler::rcmd_help(int argc, char *argv[], out_func of, data_func df) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + char buf[1000 + 1]; + char buf2[1000 + 1]; + int i = 0; + + encdec.enc_string("Remote command help:\n", buf, 1000); + of(buf); + for (i = 0; rp_remote_commands[i].name; i++) { +#ifdef WIN32 + sprintf(buf2, "%-10s %s\n", rp_remote_commands[i].name, rp_remote_commands[i].help); +#else + snprintf(buf2, 1000, "%-10s %s\n", rp_remote_commands[i].name, rp_remote_commands[i].help); +#endif + encdec.enc_string(buf2, buf, 1000); + of(buf); + } + std::vector cc = t->custom_commands(); + for (i = 0; icustom_commands[i].name, t->custom_commands[i].help); +#else + snprintf(buf2, 1000, "%-10s %s\n", cc[i].name, cc[i].help); +#endif + encdec.enc_string(buf2, buf, 1000); + of(buf); + } + return iss::Ok; +} + +/* Set function, set debug level */ +int cmd_handler::rcmd_set(int argc, char *argv[], out_func of, data_func df) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + char buf[1000 + 1]; + char buf2[1000 + 1]; + + if (argc == 1) { + sprintf(buf2, "Missing argument to set command.\n"); + encdec.enc_string(buf2, buf, 1000); + of(buf); + return iss::Ok; + } + + if (strcmp("debug", argv[1]) != 0) { + sprintf(buf2, "Undefined set command: \"%s\"\n", argv[1]); + encdec.enc_string(buf2, buf, 1000); + of(buf); + return iss::Ok; + } + + if (argc != 3) { + sprintf(buf2, "Wrong arguments for debug command.\n"); + encdec.enc_string(buf2, buf, 1000); + of(buf); + return iss::Ok; + } + + if (strcmp("0", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Unknown); + else if (strcmp("1", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Fatal); + else if (strcmp("2", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Error); + else if (strcmp("3", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Warning); + else if (strcmp("4", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Info); + else if (strcmp("5", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Debug); + else if (strcmp("6", argv[2]) == 0) + ELPP->setLoggingLevel(el::Level::Trace); + else { + sprintf(buf2, "Invalid debug level: \"%s\"\n", argv[2]); + encdec.enc_string(buf2, buf, 1000); + of(buf); + return iss::Ok; + } + + return iss::Ok; +} + +/* Target method */ +int cmd_handler::rcmd(const char * const in_buf, out_func of, data_func df) { + CLOG(TRACE, "connection")<<"executing "<<__FUNCTION__; + int count = 0; + int i; + char *args[MAXARGS]; + char *ptr; + unsigned int ch; + char buf[1000 + 1]; + char *s; + + CLOG(DEBUG, "connection")<<__FUNCTION__<<": handle_rcmd()"; + CLOG(DEBUG, "connection")<<"command '"<(in_buf); + s = buf; + while (*ptr) { + if (encdec.dec_byte(in_buf, &ch) == 0) + return iss::Err; + *s++ = ch; + ptr += 2; + } + *s = '\0'; + CLOG(DEBUG, "connection")<<"command '"<= MAXARGS) + return iss::Err; + args[count++] = ptr + 1; + } + ptr++; + } + /* Search the command table, and execute the function if found */ + CLOG(DEBUG, "connection")<<"executing target dependant command '"<custom_commands().size(); i++) { + if (strcmp(args[0], t->custom_commands()[i].name) == 0) + return t->custom_commands()[i].function(count, args, of, df); + } + + for (i = 0; rp_remote_commands[i].name; i++) { + if (strcmp(args[0], rp_remote_commands[i].name) == 0){ + int (cmd_handler::*fptr)(int, char **, out_func, data_func)=rp_remote_commands[i].function; + return (this->*fptr)(count, args, of, df); + } + } + return iss::NotSupported; + } + return iss::Err; +} + +std::string cmd_handler::handle_extended(const std::string in_buf) { + if(in_buf.find("vCtrlC", 0)==0){ + t->stop(); + return "OK"; +// to be implemented later +// } else if(in_buf.find("vCont", 0)==0){ +// if(in_buf[5]=='?') return "vCont;cst"; + } else if(in_buf.find("vRun", 0)==0){ + t->restart(); + return "S05"; + } else if(in_buf.find("vAttach", 0)==0){ + return "S05"; + } else + return ""; + // not suppported: + //vFile + //vFlashErase + //vFlashWrite + //vFlashDone +} diff --git a/src/dbgsrvbase.cpp b/src/dbgsrvbase.cpp new file mode 100644 index 0000000..f2b251e --- /dev/null +++ b/src/dbgsrvbase.cpp @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +//switch off warning C4996: 'std::copy': Function call with parameters that may be unsafe +#include +#include +#include +#include +#include "iss/debugger/server_base.h" + +using namespace iss::debugger; + +template +static T to(unsigned char* data, size_t num_bytes){ + T res = 0; + for(unsigned i=0; i::max(), std::memory_order_relaxed); + mode.store(MODE_RUN, std::memory_order_release); + syncronizer.enqueue_and_wait(&server_base::dummy_func, this); +} +// called from debugger +void server_base::request_stop(unsigned coreId){ + mode.store(MODE_STOP, std::memory_order_relaxed); +} + +void server_base::wait_for_stop() { + // busy wait + while(mode != MODE_STOP) std::this_thread::sleep_for(std::chrono::milliseconds(10)); +} + +iss::status server_base::reset(int coreId){ + return iss::Ok; +} diff --git a/src/encoderdecoder.cpp b/src/encoderdecoder.cpp new file mode 100644 index 0000000..44a3913 --- /dev/null +++ b/src/encoderdecoder.cpp @@ -0,0 +1,499 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include "iss/debugger/encoderdecoder.h" + +#include +#include +#include +#include + +using namespace iss::debugger; + +enum { + // breakpoint types + BPTYPE_MIN =0, + BPTYPE_MAX =4, + //_query mask bits + MASKBIT_THREADID =1, + MASKBIT_EXISTS =2, + MASKBIT_DISPLAY =4, + MASKBIT_THREADNAME =8, + MASKBIT_MOREDISPLAY =16 +}; + +/* Convert stream of chars into data */ +std::vector encoder_decoder::dec_data(const char *in) { + size_t count; + unsigned int bytex; + std::vector out; + assert(in != nullptr); + + for (count = 0; *in; count++, in += 2) { + if (*(in + 1) == '\0') { + /* Odd number of nibbles. Discard the last one */ + LOG(WARNING)<<__FUNCTION__<<": odd number of nibbles"; + if (count == 0) + out.clear(); + return out; + } + + if (!dec_byte(in, &bytex)){ // parse error + out.clear(); + return out; + } + out.push_back(bytex & 0xff); + } + + if (*in) // Input too long + out.clear(); + return out; +} + +int encoder_decoder::dec_reg(const char *in, unsigned int *reg_no) { + if (!dec_uint32(&in, reg_no, '\0')) + return false; + + return true; +} + +/* Decode reg_no=XXXXXX */ +std::vector encoder_decoder::dec_reg_assignment(const char *in, unsigned int *reg_no) { + assert(in != nullptr); + assert(reg_no != nullptr); + + if (!dec_uint32(&in, reg_no, '=')) + return std::vector(); + + return dec_data(in); +} + +/* Decode memory transfer parameter in the form of AA..A,LL..L */ +int encoder_decoder::dec_mem(const char *in, uint64_t *addr, size_t *len) { + assert(in != nullptr); + assert(addr != nullptr); + assert(len != 0); + if (!dec_uint64(&in, addr, ',')) + return false; + + *len = 0; + return dec_uint32(&in, (unsigned *)len, '\0'); +} + +/* Decode process query. Format: 'MMMMMMMMRRRRRRRRRRRRRRRR' + where: + M represents mask + R represents thread reference */ +int encoder_decoder::dec_process_query(const char *in, unsigned int *mask, rp_thread_ref *ref) { + unsigned int tmp_mask; + uint64_t tmp_val; + + assert(in != nullptr); + assert(mask != nullptr); + assert(ref != nullptr); + + if (!dec_4bytes(in, &tmp_mask)) + return false; + in += 8; + + if (!dec_8bytes(in, &tmp_val)) + return false; + + *mask = tmp_mask; + ref->val = tmp_val; + + return true; +} + +/* Decode thread list list query. Format 'FMMAAAAAAAAAAAAAAAA' + where: + F represents first flag + M represents max count + A represents argument thread reference */ +int encoder_decoder::dec_list_query(const char *in, int *first, size_t *max, rp_thread_ref *arg) { + unsigned int first_flag; + size_t tmp_max; + uint64_t tmp_val; + + assert(in != nullptr); + assert(first != nullptr); + assert(max != nullptr); + assert(arg != nullptr); + + if (!dec_nibble(in, &first_flag)) + return false; + in++; + + if (!dec_byte(in, (unsigned*)&tmp_max)) + return false; + in += 2; + + if (!dec_8bytes(in, &tmp_val)) + return false; + + *first = (first_flag) ? true : false; + *max = tmp_max; + arg->val = tmp_val; + + return true; +} + +/* Decode a breakpoint (z or Z) packet */ +int encoder_decoder::dec_break(const char *in, int *type, uint64_t *addr, unsigned int *len) { + unsigned int val; + + assert(in != nullptr); + assert(*in != '\0'); + assert(type != nullptr); + assert(addr != nullptr); + assert(len != nullptr); + + in++; + if (!dec_nibble(in, &val)) + return false; + in++; + + if (val < BPTYPE_MIN || val > BPTYPE_MAX) + return false; + + if (*in++ != ',') + return false; + + *type = val; + + if (!dec_uint64(&in, addr, ',')) + return false; + + if (!dec_uint32(&in, len, '\0')) + return false; + + return true; +} + +/* If a byte of avail is 0 then the corresponding data byte is + encoded as 'xx', otherwise it is encoded in normal way */ +std::string encoder_decoder::enc_regs(const std::vector& data, const std::vector& avail) { + assert(data.size() > 0); + + std::stringstream ss; + for (size_t i = 0; i < data.size(); i++) { + if (avail[i]) { + ss< 0); + + std::stringstream ss; + for (size_t i = 0; i < data_len; i++, data++) + ss<& data) { + assert(data.size() > 0); + + std::stringstream ss; + for (size_t i = 0; i < data.size(); i++) + ss< 0); + + if (strlen(s) * 2 >= out_size) { + /* We do not have enough space to encode the data */ + return false; + } + + i = 0; + while (*s) { + *out++ = hex[(*s >> 4) & 0x0f]; + *out++ = hex[*s & 0x0f]; + s++; + i++; + } + *out = '\0'; + return i; +} + +void encoder_decoder::encode_str(std::stringstream& ss, const char* const str) { + /* and Encode value */ + std::ios init(NULL); + init.copyfmt(ss); + ss << std::setw(2) << std::setfill('0') << std::hex << strlen(str); + ss.copyfmt(init); + ss << str; +} + +/* Encode result of process query: + qQMMMMMMMMRRRRRRRRRRRRRRRR(TTTTTTTTLLVV..V)*, + where + M represents mask + R represents ref + T represents tag + L represents length + V represents value */ +std::string encoder_decoder::enc_process_query_response(unsigned int mask, const rp_thread_ref *ref, const rp_thread_info *info) { + size_t len; + unsigned int tag; + int i; + assert(ref != nullptr); + assert(info != nullptr); + + std::stringstream ss; + + /* Encode header */ + ss<< "qQ"; + /* Encode mask */ + ss<val; + + for (i = 0, tag = 0; i < 32; i++, tag <<= 1) { + if ((mask & tag) == 0) + continue; + /* Encode tag */ + ss<thread_id.val; + break; + case MASKBIT_EXISTS: + /* Encode Length */ + /* and Encode value */ + ss<<"01"<<(info->exists) ? '1' : '0'; + break; + case MASKBIT_DISPLAY: + /* Encode length */ + /* and Encode value */ + encode_str(ss, info->display); + break; + case MASKBIT_THREADNAME: + /* Encode length */ + /* Encode value */ + encode_str(ss, info->thread_name); + break; + case MASKBIT_MOREDISPLAY: + /* Encode length */ + /* Encode value */ + encode_str(ss, info->more_display); + break; + default: + /* Unexpected tag value */ + assert(0); + return 0; + } + } + + return ss.str(); +} + +/* Encode result of list query: + qMCCDAAAAAAAAAAAAAAAA(FFFFFFFFFFFFFFFF)*, + where + C reprsents count + D represents done + A represents arg thread reference + F represents found thread reference(s) */ +std::string encoder_decoder::enc_list_query_response(size_t count, int done, const rp_thread_ref& arg, const std::vector found) { + assert(count <= 255); + + std::string enc_buf; + std::stringstream ss; + + /* Encode header, count, done and arg */ + ss<<"qM"; + enc_byte(count, enc_buf); + ss<= '0' && *in <= '9') + nib = *in - '0'; + if (*in >= 'A' && *in <= 'F') + nib = *in - 'A' + 10; + if (*in >= 'a' && *in <= 'f') + nib = *in - 'a' + 10; + + if (nib >= 0) { + *nibble = nib; + return true; + } + + return false; +} + +/* Decode byte */ +int encoder_decoder::dec_byte(const char *in, unsigned int *byte_ptr) { + unsigned int ls_nibble; + unsigned int ms_nibble; + + if (!dec_nibble(in, &ms_nibble)) + return false; + + if (!dec_nibble(in + 1, &ls_nibble)) + return false; + + *byte_ptr = (ms_nibble << 4) + ls_nibble; + return true; +} + +/* Decode exactly 4 bytes of hex from a longer string, and return the result + as an unsigned 32-bit value */ +int encoder_decoder::dec_4bytes(const char *in, uint32_t *val) { + unsigned int nibble; + uint32_t tmp; + int count; + + for (tmp = 0, count = 0; count < 8; count++, in++) { + if (!dec_nibble(in, &nibble)) + break; + tmp = (tmp << 4) + nibble; + } + *val = tmp; + return true; +} + +/* Decode exactly 8 bytes of hex from a longer string, and return the result + as an unsigned 64-bit value */ +int encoder_decoder::dec_8bytes(const char *in, uint64_t *val) { + unsigned int nibble; + uint64_t tmp; + int count; + + for (tmp = 0, count = 0; count < 16; count++, in++) { + if (!dec_nibble(in, &nibble)) + break; + tmp = (tmp << 4) + nibble; + } + *val = tmp; + return true; +} + +/* Decode a hex string to an unsigned 32-bit value */ +int encoder_decoder::dec_uint32(char const **in, uint32_t *val, char break_char) { + unsigned int nibble; + uint32_t tmp; + int count; + + assert(in != nullptr); + assert(val != nullptr); + + if (**in == '\0') { + /* We are expecting at least one character */ + return false; + } + + for (tmp = 0, count = 0; **in && count < 8; count++, (*in)++) { + if (!dec_nibble(*in, &nibble)) + break; + tmp = (tmp << 4) + nibble; + } + + if (**in != break_char) { + /* Wrong terminating character */ + return false; + } + if (**in) + (*in)++; + *val = tmp; + return true; +} + +/* Decode a hex string to an unsigned 64-bit value */ +int encoder_decoder::dec_uint64(char const **in, uint64_t *val, char break_char) { + unsigned int nibble; + uint64_t tmp; + int count; + + assert(in != nullptr); + assert(val != nullptr); + + if (**in == '\0') { + /* We are expecting at least one character */ + return false; + } + + for (tmp = 0, count = 0; **in && count < 16; count++, (*in)++) { + if (!dec_nibble(*in, &nibble)) + break; + tmp = (tmp << 4) + nibble; + } + + if (**in != break_char) { + /* Wrong terminating character */ + return false; + } + if (**in) + (*in)++; + *val = tmp; + return true; +} + +/* Encode byte */ +void encoder_decoder::enc_byte(unsigned char val, std::string& out, size_t offset) { + const char hex[] = "0123456789abcdef"; + out.reserve(offset+2); + out[offset] = hex[(val >> 4) & 0xf]; + out[offset + 1] = hex[val & 0xf]; +} + diff --git a/src/gdb_session.cpp b/src/gdb_session.cpp new file mode 100755 index 0000000..e066040 --- /dev/null +++ b/src/gdb_session.cpp @@ -0,0 +1,316 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +namespace iss { +namespace debugger { +class packet_parse_error: public std::exception { +}; +} +} +using namespace iss::debugger; +using namespace std; + +const int THREAD_ID = 1; +const int CORE_ID = 0; +const std::string ack("+"); +const std::string sig_trap("S05"); + +template +bool begins_with(const TContainer& input, const TContainer& match) { + return input.size() >= match.size() && equal(match.begin(), match.end(), input.begin()); +} + +inline +bool compare(string input, string match, size_t offset = 0) { + return (input.length() >= (offset + match.length())) && (input.compare(offset, match.length(), match) == 0); +} + +inline std::vector split(const std::string& s, char seperator) { + std::vector output; + std::string::size_type prev_pos = 0, pos = 0; + while ((pos = s.find(seperator, pos)) != std::string::npos) { + std::string substring(s.substr(prev_pos, pos - prev_pos)); + output.push_back(substring); + prev_pos = ++pos; + } + output.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word + return output; +} + +struct gdb_resp_msg { + gdb_resp_msg() : + check_sum(0), body(false) { + } + void add(uint8_t m) { + if (m == '#' || m == '$' || m == '}') { + buffer.push_back('}'); + m ^= 0x20; + } + buffer.push_back(m); + check_sum += m; + } + char get_checksum_char(int idx) const { + assert(idx == 0 || idx == 1); + const char c = (check_sum >> (4 - 4 * idx)) & 0x0F; + assert(c < 16); + return c > 9 ? (c + 'a' - 10) : (c + '0'); + } + gdb_resp_msg & operator <<(const char *msg) { + body = true; + while (msg && *msg) + add(*(msg++)); + return *this; + } + template + gdb_resp_msg & operator <<(T val) { + stringstream ss; + ss << std::hex << val; + this->operator <<(ss.str().c_str()); + return *this; + } + unsigned char operator [](unsigned int idx) const { + return buffer.at(idx); + } + operator bool() { + return /*ack||*/body; + } + operator std::string() { + std::stringstream ss; + if (body) { + ss << "$"; + for (std::vector::const_iterator it = buffer.begin(); it != buffer.end(); ++it) + ss << *it; + ss << "#" << get_checksum_char(0) << get_checksum_char(1); + } + return ss.str(); + } +protected: + std::vector buffer; + uint8_t check_sum; + bool body; +}; + +int gdb_session::start() { + conn_shptr->add_listener(shared_from_this()); + boost::asio::ip::tcp::endpoint endpoint = conn_shptr->socket().remote_endpoint(); + CLOG(TRACE, "connection") << "gdb_session::start(), got connected to remote port " << endpoint.port() << " on " + << endpoint.address().to_string(); + conn_shptr->async_read(); //start listening + return 0; +} + +void gdb_session::send_completed(const boost::system::error_code& e) { + if (!e) { + conn_shptr->async_read(); + } else { + LOG(ERROR) << e.message() << "(" << e << ")"; + } +} + +bool is_all_hex(char* input) { //destroys input + return (strtok(input, "0123456789ABCDEFabcdef") == NULL); +} + +bool gdb_session::message_completed(std::vector& buffer) { +// std::cout << "Checking for "; for (auto i = buffer.begin(); i != buffer.end(); ++i) std::cout << (int)*i << '('<<*i<<") ";std::cout< 4 && buffer[0] == '$' && buffer[s - 3] == '#') { + return true; + } + return false; +} + +void gdb_session::receive_completed(const boost::system::error_code& e, std::string* msg) { + if (e.value() == 2) { + CLOG(WARNING, "connection") << "Client closed connection (" << e.message() << ")"; + //TODO: cleanup settings like: server.remove_breakpoint(CORE_ID, 0); + handler.t->close(); + return; + } else if (e) { + CLOG(ERROR, "connection") << "Communication error (" << e.message() << ")"; + handler.t->close(); + return; + } + if (msg->compare("+") == 0) { + CLOG(TRACE, "connection") << "Received ACK"; + last_msg = ""; + conn_shptr->async_read(); + } else if (msg->compare("-") == 0) { + CLOG(TRACE, "connection") << "Received NACK, repeating msg '" << last_msg << "'"; + conn_shptr->write_data(&last_msg); + conn_shptr->async_read(); + } else if (msg->at(0) == -1 && msg->at(1) == -13) { + CLOG(TRACE, "connection") << "Received BREAK, interrupting target"; + handler.t->stop(); + respond("S05"); + } else { + CLOG(TRACE, "connection") << "Received packet '" << *msg << "', processing it"; + std::string data = check_packet(*msg); + if (data.size()) { + conn_shptr->write_data(&ack); + parse_n_execute(data); + } else + respond("-"); + } + return; +} + +void gdb_session::parse_n_execute(std::string& data) { + try { + gdb_resp_msg resp; + std::string out_buf; + int do_connect; + bool do_reinitialize=false, input_error=false; + switch (data[0]) { + case '!': + /* Set extended operation */ + CLOG(DEBUG, "connection")<<__FUNCTION__<<": switching to extended protocol mode"; + if (handler.can_restart) { + handler.extended_protocol = true; + resp<<"OK"; + } else { + /* Some GDBs will accept any response as a good one. Let us bark in the log at least */ + CLOG(ERROR, "connection")<<__FUNCTION__<<": extended operations required, but not supported"; + } + break; + case '?': + /* Report the last signal status */ + resp<<"T0"<< ABORTED; + break; + case 'A': + /* Set the argv[] array of the target */ + //TODO: implement this! + break; + case 'C': + case 'S': + case 'c': + case 's': + resp<> std::hex >> xmit; + if (xmit == (checksum & 0xff)) + return msg.substr(start + 1, end - 1); + //throw(iss::debugger::packet_parse_error()); + return ""; +} diff --git a/src/target_adapter_base.cpp b/src/target_adapter_base.cpp new file mode 100644 index 0000000..b06fd65 --- /dev/null +++ b/src/target_adapter_base.cpp @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include + +using namespace iss::debugger; + +void target_adapter_base::help(const char* prog_name) { +} + +iss::status target_adapter_base::open(int argc, char* const agrv[], const char* prog_name, log_func log_fn) { + return iss::Ok; +} + +void target_adapter_base::close(void) { + bp_lut.clear(); +} + +iss::status target_adapter_base::connect(std::string& status_string, bool& can_restart) { + return iss::Ok; +} + +iss::status target_adapter_base::disconnect(void) { + return iss::Ok; +} + +void target_adapter_base::kill(void) { +} + +iss::status target_adapter_base::restart(void) { + return iss::Ok; +} + +void target_adapter_base::stop(void) { + srv->request_stop(0); + srv->wait_for_stop(); +} + +iss::status target_adapter_base::resume_from_current(bool step, int sig) { + if(step) + srv->step(0, 1); + else + srv->run(0); + return iss::Ok; +} + +iss::status target_adapter_base::wait_non_blocking(std::string& status_string, out_func out, bool& running) { + running=srv->is_running(); + if(running){ + std::this_thread::sleep_for(std::chrono::seconds(1)); + running=srv->is_running(); + } + return iss::NotSupported; +} + +iss::status target_adapter_base::wait_blocking(std::string& status_string, out_func out) { + srv->wait_for_stop(); + return iss::Ok; +} + diff --git a/src/vm_base.cpp b/src/vm_base.cpp new file mode 100644 index 0000000..1a9eb77 --- /dev/null +++ b/src/vm_base.cpp @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace std; + +namespace iss { +namespace vm { + +llvm::cl::opt LikelyBranchWeight("likely-branch-weight", llvm::cl::Hidden, llvm::cl::init(64), + llvm::cl::desc("Weight of the branch likely to be taken (default = 64)")); +llvm::cl::opt UnlikelyBranchWeight("unlikely-branch-weight", llvm::cl::Hidden, llvm::cl::init(4), + llvm::cl::desc("Weight of the branch unlikely to be taken (default = 4)")); + +} +} +