From 0f669132a000a33dd545e639df2438124ba00998 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 14 Jun 2017 14:19:41 -0700 Subject: [PATCH] kernel: remove gdb_server This is unmaintained and currently has no known users. It was added to support a Wind River project. If in the future we need it again, we should re-introduce it with an exception-based mechanism for catching out-of-bounds memory queries from the debugger. The mem_safe subsystem is also removed, it is only used by the GDB server. If its functionality is needed in the future, it shoudl be replaced with an exception-based mechanism. The _image_{ram, rom, text}_{start, end} linker variables have been left in place, they will be re-purposed and expanded to support memory protection. Signed-off-by: Andrew Boie --- include/arch/arm/cortex_m/scripts/linker.ld | 4 - include/arch/x86/linker.ld | 5 - include/debug/mem_safe.h | 154 -- subsys/debug/Kconfig | 96 - subsys/debug/Makefile | 5 +- subsys/debug/gdb_server.c | 2461 ----------------- subsys/debug/mem_safe_check_boundaries.c | 283 -- tests/kernel/mem_safe/Makefile | 13 - tests/kernel/mem_safe/README.txt | 96 - tests/kernel/mem_safe/prj_qemu_cortex_m3.conf | 3 - tests/kernel/mem_safe/prj_qemu_x86.conf | 3 - tests/kernel/mem_safe/src/Makefile | 6 - tests/kernel/mem_safe/src/asm_arm.S | 0 tests/kernel/mem_safe/src/asm_x86.S | 19 - tests/kernel/mem_safe/src/main.c | 410 --- tests/kernel/mem_safe/testcase.ini | 5 - 16 files changed, 1 insertion(+), 3562 deletions(-) delete mode 100644 include/debug/mem_safe.h delete mode 100644 subsys/debug/gdb_server.c delete mode 100644 subsys/debug/mem_safe_check_boundaries.c delete mode 100644 tests/kernel/mem_safe/Makefile delete mode 100644 tests/kernel/mem_safe/README.txt delete mode 100644 tests/kernel/mem_safe/prj_qemu_cortex_m3.conf delete mode 100644 tests/kernel/mem_safe/prj_qemu_x86.conf delete mode 100644 tests/kernel/mem_safe/src/Makefile delete mode 100644 tests/kernel/mem_safe/src/asm_arm.S delete mode 100644 tests/kernel/mem_safe/src/asm_x86.S delete mode 100644 tests/kernel/mem_safe/src/main.c delete mode 100644 tests/kernel/mem_safe/testcase.ini diff --git a/include/arch/arm/cortex_m/scripts/linker.ld b/include/arch/arm/cortex_m/scripts/linker.ld index a20e5f0adb9..2f4b67f1f08 100644 --- a/include/arch/arm/cortex_m/scripts/linker.ld +++ b/include/arch/arm/cortex_m/scripts/linker.ld @@ -180,8 +180,6 @@ SECTIONS { _image_ram_start = .; __data_ram_start = .; - *(.top_of_image_ram) - *(.top_of_image_ram.*) *(.data) *(".data.*") @@ -222,8 +220,6 @@ SECTIONS */ *(.noinit) *(".noinit.*") - *(.bottom_of_image_ram) - *(.bottom_of_image_ram.*) } GROUP_LINK_IN(RAMABLE_REGION) /* Define linker symbols */ diff --git a/include/arch/x86/linker.ld b/include/arch/x86/linker.ld index 0b808cbec30..0cde0c37e3c 100644 --- a/include/arch/x86/linker.ld +++ b/include/arch/x86/linker.ld @@ -130,8 +130,6 @@ SECTIONS KEXEC_PGALIGN_PAD(MMU_PAGE_SIZE) _image_ram_start = .; __data_ram_start = .; - *(.top_of_image_ram) - *(.top_of_image_ram.*) *(.data) *(".data.*") @@ -193,9 +191,6 @@ SECTIONS */ *(.noinit) *(".noinit.*") - - *(.bottom_of_image_ram) - *(.bottom_of_image_ram.*) } GROUP_LINK_IN(RAMABLE_REGION) diff --git a/include/debug/mem_safe.h b/include/debug/mem_safe.h deleted file mode 100644 index 88ccabf18c2..00000000000 --- a/include/debug/mem_safe.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef _debug__mem_safe__h_ -#define _debug__mem_safe__h_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @file - * @brief Safe memory access routines - * - * This module provides functions for safely writing to and reading from - * memory, as well as probing if a memory address is accessible. For the - * latter, permissions can be specified (read/write). - */ - -#define SYS_MEM_SAFE_READ 0 -#define SYS_MEM_SAFE_WRITE 1 - -/** - * @brief verify memory at an address is accessible - * - * Probe memory for read or write access by specifying the @a perm, either - * SYS_MEM_SAFE_WRITE or SYS_MEM_SAFE_READ. A size @a num_bytes specifying - * the number of bytes to access must also be specified: for a 32-bit system, - * it can only take the values 1,2 or 4 (8/16/32 bits). Both @a p and @a buf - * must be naturally aligned to @a num_bytes. - * - * On a read, the value read from the memory location @a p is returned through - * @a buf; on a write, the value contained in @a buf is written to memory at - * memory location @a p. - * - * @param p The pointer (address) to the location to probe - * @param perm Either SYS_MEM_PROBE_READ or SYS_MEM_PROBE_WRITE - * @param num_bytes The number of bytes to probe, must be either 1, 2 or 4 - * @param buf On reads, will contain the value read; on writes, contains the - * value to write - * - * @retval 0 OK - * @retval -EINVAL If an invalid parameter is passed - * @retval -EFAULT If the address is not accessible. - */ - -extern int _mem_probe(void *p, int perm, size_t num_bytes, void *buf); - -/** - * @brief safely read memory - * - * @details Read @a num_bytes bytes at address @a src from buffer pointed to - * by @a buf. The @a width parameter specifies bus width of each memory access - * in bytes. If @a width is 0 a target optimal access width is used. All other - * values of access width are target dependent and optional. When @a width is - * non-zero, the both @a num_bytes and @a src must be a multiple of @a width. - * - * @param src The address to read from - * @param buf The destination buffer to receive the data read - * @param num_bytes The number of bytes to read - * @param width The access width - * - * @retval 0 OK - * @retval -EFAULT If there was an error reading memory - * @retval -EINVAL If access width, num_bytes and addresses are not compatible. - */ - -extern int _mem_safe_read(void *src, char *buf, size_t num_bytes, - int width); - -/** - * @brief safely write memory - * - * @details Write @a num_bytes bytes to address @a dest from buffer pointed to - * by @a buf. The @a width parameter specifies bus width of each memory access - * in bytes. If @a width is 0 a target optimal access width is used. All other - * values of access width are target dependent and optional. When @a width is - * non-zero, the both @a num_bytes and @a dest must be a multiple of @a width. - * - * @param dest The address to write to - * @param buf The source buffer to write in memory - * @param num_bytes The number of bytes to write - * @param width The access width - * - * @retval 0 OK - * @retval -EFAULT If there was an error writing memory - * @retval -EINVAL If access width, num_bytes and addresses are not compatible. - */ - -extern int _mem_safe_write(void *dest, char *buf, size_t num_bytes, - int width); - -/** - * @brief write to text section - * - * @details Write @a num_bytes bytes to address @a dest from buffer pointed to - * by @a buf. - * - * @param dest The address to write to - * @param buf The source buffer to write in memory - * @param num_bytes The number of bytes to write - * - * @retval 0 Success - * @retval -EFAULT If there was an error writing memory - */ - -extern int _mem_safe_write_to_text_section(void *dest, char *buf, - size_t num_bytes); - -/** - * @brief add to the table of valid regions - * - * @details Add a new region that is considered valid to read from or both - * read from and write to. The region starts at @a addr and its size is @a - * num_bytes. The read/write permissions are specified via @a perm and can - * take the values either SYS_MEM_SAFE_READ or SYS_MEM_SAFE_WRITE. - * - * The table size is specified via the CONFIG_MEM_SAFE_NUM_EXTRA_REGIONS kconfig - * option. - * - * If the implementation of safe memory access chosen does not need this API, - * it is still available, but results in a no-op and always returns success - * (0). - * - * @param addr The address to write to - * @param num_bytes The size of the region in bytes - * @param perm The access permissions, either SYS_MEM_SAFE_WRITE or - * SYS_MEM_SAFE_READ - * - * @retval 0 OK - * @retval -ENOMEM If there there is no space left in the table of regions - * @retval -EINVAL If passing invalid permissions - */ - -#ifdef CONFIG_MEM_SAFE_CHECK_BOUNDARIES -extern int _mem_safe_region_add(void *addr, size_t num_bytes, int perm); -#else -static inline int _mem_safe_region_add(void *addr, size_t num_bytes, - int perm) -{ - return 0; -} -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _debug__mem_safe__h_ */ diff --git a/subsys/debug/Kconfig b/subsys/debug/Kconfig index 27a70a7db41..7b97acff5a2 100644 --- a/subsys/debug/Kconfig +++ b/subsys/debug/Kconfig @@ -151,53 +151,6 @@ config OMIT_FRAME_POINTER If unsure, disable OVERRIDE_FRAME_POINTER_DEFAULT to allow the compiler to adopt sensible defaults for your architecture. -menu "Safe memory access" - -config MEM_SAFE - bool - prompt "Enable safe memory access" - default n - help - Add the routines available in mem_safe.h to the system. This is added - as a kconfig option instead of simply linking against the library - because some implementations might require initialization. - -choice -prompt "Safe memory access implementation" -depends on MEM_SAFE -default MEM_SAFE_CHECK_BOUNDARIES - -config MEM_SAFE_CHECK_BOUNDARIES - bool - prompt "Software validation of memory access within memory regions" - help - This implementation checks the application image's text/rodata - boundaries for its read-only region and the data/bss/noinit boundaries - for its read-write region, in software. - - Other regions can be added as needed by using the - sys_mem_safe_region_add() API. The number of regions that can be added - is controlled via the MEM_SAFE_NUM_EXTRA_REGIONS kconfig option. - - This implementation requires initialization and thus consumes some boot - time. - -endchoice - -config MEM_SAFE_NUM_EXTRA_REGIONS - int - prompt "Number of safe memory access regions to be added at runtime" - depends on MEM_SAFE_CHECK_BOUNDARIES - default 0 - help - The functions available in mem_safe.h check if memory is within - read-only or read-write regions before accessing it instead of crashing. - The kernel image is added as a valid region automatically, but other - regions can be added if the application makes access to additional - memory outside of the image's boundaries. - -endmenu - # # Generic Debugging Options # @@ -211,55 +164,6 @@ config DEBUG_INFO NOTE: Does not currently work with the x86 IAMCU ABI. -# -# GDB Server options -# - -config GDB_SERVER - bool - prompt "Enable GDB Server [EXPERIMENTAL]" - default n - select CACHE_FLUSHING - select REBOOT - select MEM_SAFE - select DEBUG_INFO - select UART_CONSOLE_DEBUG_SERVER_HOOKS - help - This option enables the GDB Server support. - -config GDB_SERVER_MAX_SW_BP - int "Maximum number of GDB Server Software breakpoints" - default 100 - depends on GDB_SERVER - help - This option specifies the maximum number of Software breakpoints - -config GDB_SERVER_INTERRUPT_DRIVEN - bool - prompt "Enable GDB interrupt mode" - default y - depends on GDB_SERVER - select CONSOLE_HANDLER - help - This option enables interrupt support for GDB Server. - -config GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR - string - prompt "Trigger string for remote serial ext. via notifi. packets" - default "WrCons" - depends on GDB_SERVER - help - The value of this option depends on the string the GDB client use to - prefix the notification packets. - -config GDB_SERVER_BOOTLOADER - bool - prompt "Enable the bootloader mode" - default n - depends on GDB_SERVER - help - This option enables the bootloader mode of the GDB Server. - # # Miscellaneous debugging options # diff --git a/subsys/debug/Makefile b/subsys/debug/Makefile index 4fe055e094a..37423564f59 100644 --- a/subsys/debug/Makefile +++ b/subsys/debug/Makefile @@ -1,9 +1,6 @@ ccflags-y +=-I$(srctree)/include/debug -CFLAGS_gdb_server.o =-I$(srctree)/include/drivers -obj-y = -obj-$(CONFIG_MEM_SAFE_CHECK_BOUNDARIES) += mem_safe_check_boundaries.o -obj-$(CONFIG_GDB_SERVER) += gdb_server.o +obj- = dummy.o ifeq ($(CONFIG_OPENOCD_SUPPORT),y) lib-y += openocd.o diff --git a/subsys/debug/gdb_server.c b/subsys/debug/gdb_server.c deleted file mode 100644 index 08c5edd2b7c..00000000000 --- a/subsys/debug/gdb_server.c +++ /dev/null @@ -1,2461 +0,0 @@ -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file Generic part of the GDB server - * - * This module provides the embedded GDB Remote Serial Protocol for Zephyr. - * - * The following is a list of all currently defined GDB RSP commands. - * - * `?' - * Indicate the reason the target halted. - * - * `c [addr]' - * Continue. addr is address to resume. If addr is omitted, resume at - * current address. - * - * `C sig[;addr]' - * Continue with signal sig (hex signal number). If `;addr' is omitted, - * resume at same address. - * - * _WRS_XXX - Current limitation: Even if this syntax is understood, the - * GDB server does not resume the context with the specified signal but - * resumes it with the exception vector that caused the context to stop. - * - * `D' - * Detach GDB from the remote system. - * - * `g' - * Read general registers. - * - * `G XX...' - * Write general registers. - * - * `k' - * Detach GDB from the remote system. - * - * `m addr,length' - * Read length bytes of memory starting at address addr. Note that addr - * may not be aligned to any particular boundary. The stub need not use - * any particular size or alignment when gathering data from memory for - * the response; even if addr is word-aligned and length is a multiple of - * the word size, the stub is free to use byte accesses, or not. For this - * reason, this packet may not be suitable for accessing memory-mapped I/O - * devices. - * - * `M addr,length:XX...' - * Write length bytes of memory starting at address addr. XX... is the data. - * Each byte is transmitted as a two-digit hexadecimal number. - * - * `p n' - * Read the value of register n; n is in hex. - * - * `P n...=r...' - * Write register n... with value r.... The register number n is in - * hexadecimal, and r... contains two hex digits for each byte in the - * register (target byte order). - * - * `q name params...' - * General query. See General Query Packets description - * - * `s [addr]' - * Single step. addr is the address at which to resume. If addr is omitted, - * resume at same address. - * - * `S sig[;addr]' - * Step with signal. This is analogous to the `C' packet, but requests a - * single-step, rather than a normal resumption of execution. - * - * NOTE: Current limitation: Even if this syntax is understood, the GDB - * server steps the context without the specified signal (i.e like the - * `s [addr]' command). - * - * `T thread-id' - * Find out if the thread thread-id is alive. - * - * `vCont[;action[:thread-id]]...' - * Resume the inferior, specifying different actions for each thread. If an - * action is specified with no thread-id, then it is applied to any threads - * that don't have a specific action specified; if no default action is - * specified then other threads should remain stopped. Specifying multiple - * default actions is an error; specifying no actions is also an error. - * - * Currently supported actions are: - * - * `c' - * Continue. - * `C sig' - * Continue with signal sig. The signal sig should be two hex digits. - * `s' - * Step. - * `S sig' - * Step with signal sig. The signal sig should be two hex digits. - * - * The optional argument addr normally associated with the `c', `C', `s', - * and `S' packets is not supported in `vCont'. - * - * `X addr,length:XX...' - * Write length bytes of memory starting at address addr, where the data - * is transmitted in binary. The binary data representation uses 7d (ascii - * ‘}’) as an escape character. Any escaped byte is transmitted as the - * escape character followed by the original character XORed with 0x20. - * - * `z type,addr,length' - * `Z type,addr,length' - * Insert (`Z') or remove (`z') a type breakpoint starting at addr address - * of length length. - * - * General Query Packets: - * `qC' - * Return the current thread ID. - * - * `qSupported' - * Query the GDB server for features it supports. This packet allows - * client to take advantage of GDB server's features. - * - * These are the currently defined GDB server features, in more detail: - * - * `PacketSize=bytes' - * The GDB server can accept packets up to at least bytes in length. - * client will send packets up to this size for bulk transfers, and - * will never send larger packets. This is a limit on the data - * characters in the packet, including the frame and checksum. There - * is no trailing NUL byte in a remote protocol packet; - * - * `qXfer:features:read' - * Access the target description. Target description can identify the - * architecture of the remote target and (for some architectures) - * provide information about custom register sets. They can also - * identify the OS ABI of the remote target. Client can use this - * information to autoconfigure for your target, or to warn you if you - * connect to an unsupported target. - * - * By default, the following simple target description is supported: - * - * - * i386 - * - * - * But architectures may also reports information on specific features - * such as extended registers definitions or hardware breakpoint - * definitions. - * - * Each `' describes some logical portion of the target - * system. - * A `' element has this form: - * - * - * [TYPE...] - * REG... - * - * - * Each feature's name should be unique within the description. The - * name of a feature does not matter unless GDB has some special - * knowledge of the contents of that feature; if it does, the feature - * should have its standard name. - * - * Extended registers definitions are reported following the standard - * register format defined by GDB Remote protocol: - * - * Each register is represented as an element with this form: - * - * - * - * The components are as follows: - * - * NAME - * The register's name; it must be unique within the target - * description. - * - * BITSIZE - * The register's size, in bits. - * - * REGNUM - * The register's number. If omitted, a register's number is one - * greater than that of the previous register (either in the - * current feature or in a preceding feature); the first register - * in the target description defaults to zero. This register - * number is used to read or write the register; e.g. it is used - * in the remote `p' and `P' packets, and registers appear in the - * `g' and `G' packets in order of increasing register number. - * - * SAVE-RESTORE - * Whether the register should be preserved across inferior - * function calls; this must be either `yes' or `no'. The default - * is `yes', which is appropriate for most registers except for - * some system control registers; this is not related to the - * target's ABI. - * - * TYPE - * The type of the register. TYPE may be a predefined type, a - * type defined in the current feature, or one of the special - * types `int' and `float'. `int' is an integer type of the - * correct size for BITSIZE, and `float' is a floating point type - * (in the architecture's normal floating point format) of the - * correct size for BITSIZE. The default is `int'. - * - * GROUP - * The register group to which this register belongs. GROUP must - * be either `general', `float', or `vector'. If no GROUP is - * specified, GDB will not display the register in `info - * registers'. - * - * - * Hardware breakpoint definitions are reported using the following - * format: - * - * - * - * HW_BP_DESC... - * - * - * The defaults section allows to define some default values and avoid - * to list them in each HW_BP_DESC. - * - * Each HW_BP_DESC entry has the form: - * - * - * - * If HW_BP_DESC defines an item which has a default value defined, - * then it overwrite the default value for HW_BP_DESC entry. - * - * Items in [brackets] are optional. The components are as follows: - * - * MAX_BP - * Maximum number of hardware breakpoints that can be set. - * - * MAX_INST_BP - * Maximum number of instruction hardware breakpoints that can be - * set. - * - * MAX_WATCH_BP - * Maximum number of data hardware breakpoints that can be set. - * - * LENGTH - * Supported access lengths (in hexadecimal without 0x prefix). - * Access lengths are encoded as powers of 2 which can be OR'ed. - * For example, if an hardware breakpoint type supports 1, 2, 4, - * 8 bytes access, length will be f (0x1|0x2|0x4|0x8). - * - * ACCESS_TYPE - * Hardware breakpoint type: - * inst : Instruction breakpoint - * watch : Write access breakpoint - * rwatch: Read access breakpoint - * awatch: Read|Write access breakpoint - * - * The GDB server can also reports additional information using the - * "WR_AGENT_FEATURE" feature. The purpose of this feature is to report - * information about the agent configuration. - * The GDB server feature is using the following format: - * - * - * - * - * - * The components are as follows: - * - * MAX_SW_BP - * Maximum number of software breakpoint that can be set. - * - * STEP_ONLY_ON_BP - * This parameter is set to 1 if the GDB server is only able to - * step the context which hit the breakpoint. - * This parameter is set to 0 if the GDB server is able to step - * any context. - * - * `QStartNoAckMode' - * By default, when either the client or the server sends a packet, - * the first response expected is an acknowledgment: either `+' (to - * indicate the package was received correctly) or `-' (to request - * retransmission). This mechanism allows the GDB remote protocol to - * operate over unreliable transport mechanisms, such as a serial - * line. - * - * In cases where the transport mechanism is itself reliable (such as - * a pipe or TCP connection), the `+'/`-' acknowledgments are - * redundant. It may be desirable to disable them in that case to - * reduce communication overhead, or for other reasons. This can be - * accomplished by means of the `QStartNoAckMode' packet. - * - * `CONFIG_GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR` - * This parameter indicates that the GDB server supports transfer of - * Zephyr console I/O to the client using GDB notification packets. - * - * NOTE: Current limitation: For now, the GDB server only supports the - * console output. - * - * Notification Packets: - * Extension of the GDB Remote Serial Protocol uses notification packets - * (See `CONFIG_GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR` support). - * Those packets are transferred using the following format: - * %# - * - * For example: - * %CONFIG_GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR:# - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_GDB_SERVER_INTERRUPT_DRIVEN -#include -#endif -#ifdef CONFIG_REBOOT -#include -#endif - -#define STUB_OK "OK" -#define STUB_ERROR "ENN" - -/* Size of notification data buffers */ -#ifndef GDB_NOTIF_DATA_SIZE -#define GDB_NOTIF_DATA_SIZE 100 -#endif - -/* Overhead size for notification packet encoding. */ -#define NOTIF_PACKET_OVERHEAD 6 - -/* Maximum number of software breakpoints */ -#define MAX_SW_BP CONFIG_GDB_SERVER_MAX_SW_BP - -#define GDB_INVALID_REG_SET ((void *)-1) - -#define fill_output_buffer(x) strncpy((char *)gdb_buffer, x, GDB_BUF_SIZE - 1) - -#ifdef CONFIG_GDB_SERVER_BOOTLOADER -#define STR_TYPE ";type=zephyr_boot" -#else -#define STR_TYPE ";type=zephyr" -#endif - -#ifdef GDB_ARCH_HAS_RUNCONTROL -#define RESUME_SYSTEM() resume_system() -#define REMOVE_ALL_INSTALLED_BREAKPOINTS() \ - remove_all_installed_breakpoints() -#define UNINSTALL_BREAKPOINTS() uninstall_breakpoints() -#else -#define RESUME_SYSTEM() -#define REMOVE_ALL_INSTALLED_BREAKPOINTS() -#define UNINSTALL_BREAKPOINTS() -#endif - -#ifdef GDB_ARCH_HAS_RUNCONTROL -struct bp_array { - gdb_instr_t *addr; /* breakpoint address */ - gdb_instr_t instr; /* saved instruction */ - char valid; /* breakpoint is valid? */ - char enabled; /* breakpoint is enabled? */ -}; -#endif - -static const unsigned char hex_chars[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' -}; - -static int client_is_connected; -static int in_no_ack_mode; -static int valid_registers; -static volatile int event_is_pending; -static volatile int cpu_stop_signal = GDB_SIG_NULL; -static volatile int cpu_pending_signal; -static struct gdb_reg_set gdb_regs; - -static const char *xml_target_header = - " " - " \n"; - -static const char *xml_target_footer = ""; -static unsigned char gdb_buffer[GDB_BUF_SIZE]; - -static unsigned char tmp_reg_buffer[GDB_NUM_REG_BYTES]; - -#ifdef GDB_ARCH_HAS_RUNCONTROL - -#ifdef GDB_ARCH_HAS_HW_BP -static int hw_bp_cnt; -static struct gdb_debug_regs dbg_regs; -#endif -static int trace_lock_key; - -/* - * GDB breakpoint table. Note that all the valid entries in the breakpoint - * table are kept contiguous. When parsing the table, the first invalid entry - * in the table marks the end of the table. - */ -static struct bp_array bp_array[MAX_SW_BP]; - -#ifdef GDB_ARCH_NO_SINGLE_STEP -static gdb_instr_t *gdb_step_emu_next_pc; -static gdb_instr_t gdb_step_emu_instr; -#endif - -#endif - -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS -static volatile int notif_pkt_pending; -static volatile int notif_data_idx; -static unsigned char notif_data[GDB_NOTIF_DATA_SIZE]; -#endif - -#ifdef CONFIG_GDB_SERVER_INTERRUPT_DRIVEN -static struct k_fifo avail_queue; -static struct k_fifo cmds_queue; -#endif - -static struct device *uart_console_dev; - -/* global definitions */ - -volatile int gdb_debug_status = NOT_DEBUGGING; - -#ifdef GDB_ARCH_HAS_RUNCONTROL -#ifdef GDB_ARCH_HAS_HW_BP -volatile int cpu_stop_bp_type = GDB_SOFT_BP; -long cpu_stop_hw_bp_addr; -#endif -#endif - -static int put_packet(unsigned char *buffer); -static void put_debug_char(unsigned char ch); -static unsigned char get_debug_char(void); - -static void post_event(void); -static void control_loop(void); -static void handle_system_stop(NANO_ISF *reg, int sig); - -#ifdef GDB_ARCH_HAS_RUNCONTROL - -#define ADD_DEL_BP_SIG(x) \ - int(x)(enum gdb_bp_type type, long addr, int len, \ - enum gdb_error_code *err) - -typedef ADD_DEL_BP_SIG (add_del_bp_t); -static ADD_DEL_BP_SIG(add_bp); -static ADD_DEL_BP_SIG(delete_bp); - -static void resume_system(void); -static int set_instruction(void *addr, gdb_instr_t *instruction, size_t size); -static void remove_all_installed_breakpoints(void); -#endif - -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS -static void handle_notification(void); -static void request_notification_packet_flush(void); -static u32_t write_to_console(char *buf, u32_t len); -#endif - -#ifdef CONFIG_GDB_SERVER_INTERRUPT_DRIVEN -static int console_irq_input_hook(u8_t ch); -#endif - -static int get_hex_char_value(unsigned char ch) -{ - if ((ch >= 'a') && (ch <= 'f')) { - return ch - 'a' + 10; - } - if ((ch >= '0') && (ch <= '9')) { - return ch - '0'; - } - if ((ch >= 'A') && (ch <= 'F')) { - return ch - 'A' + 10; - } - return -1; -} - -/** - * @brief Consume a hex number from a string and convert to long long number - * - * Consume the string up to the end of the hex number, i.e. the pointer to the - * string is advanced and output that number via the @a value parameter. - * - * Does not handle negative numbers. - * - * @return The number of characters consumed from the string. - */ -static int hex_str_to_longlong(unsigned char **hex_str, long long *value) -{ - int num_chars_consumed = 0; - int hex_value; - - *value = 0; - while (**hex_str) { - hex_value = get_hex_char_value(**hex_str); - if (hex_value < 0) { - break; - } - *value = (*value << 4) | hex_value; - num_chars_consumed++; - (*hex_str)++; - } - return num_chars_consumed; -} - -/* - * Similar to hex_str_to_longlong, but outputs an int and handles negative - * numbers. - */ -static int hex_str_to_int(unsigned char **ptr, int *value) -{ - int num_chars_consumed = 0; - int hex_value; - int neg = 0; - - *value = 0; - - if (**ptr == '-') { - neg = 1; - (*ptr)++; - num_chars_consumed++; - } - - while (**ptr) { - hex_value = get_hex_char_value(**ptr); - if (hex_value < 0) { - break; - } - *value = (*value << 4) | hex_value; - num_chars_consumed++; - (*ptr)++; - } - - if (neg) { - if (num_chars_consumed == 1) { - (*ptr)--; - num_chars_consumed = 0; - } else { - *value = -(*value); - } - } - return num_chars_consumed; -} - -/* - * Consume two hex characters from a string and return the corresponding - * value. - */ -static int hex_str_to_byte(unsigned char **str) -{ - unsigned char *ptr = *str; - int byte; - - byte = get_hex_char_value(*ptr++); - byte = (byte << 4) + get_hex_char_value(*ptr++); - if (byte >= 0) { - *str = ptr; - } - return byte; -} - -/* - * Turn a one-byte value into two hex characters and write them to the buffer. - * Return the next position in the buffer. - */ - -static unsigned char *put_hex_byte(unsigned char *buf, int value) -{ - *buf++ = hex_chars[value >> 4]; - *buf++ = hex_chars[value & 0xf]; - return buf; -} - -static inline int is_size_encoder(int num) -{ - /* - * It is not possible to use the '$', '#' and '%' characters to encode - * the size per GDB remote protocol specification. - */ - return (num + 29) != '$' && (num + 29) != '#' && (num + 29) != '%'; -} - -/* - * Compress memory pointed to by buffer into its ascii hex value, the - * character '*' and the number of times it is reapeated, placing result in - * the same buffer. Return a pointer to the last char put in buf (null). - */ - -static unsigned char *compress(unsigned char *buf) -{ - unsigned char *read_ptr = buf; - unsigned char *write_ptr = buf; - unsigned char ch; - size_t count = strlen((char *)buf); - int max_repeat = 126 - 29; - size_t ix; - - for (ix = 0; ix < count; ix++) { - int num = 0; - int jx; - - ch = *read_ptr++; - *write_ptr++ = ch; - for (jx = 1; ((jx + ix) < count) && (jx < max_repeat); jx++) { - if (read_ptr[jx - 1] == ch) { - num++; - } else { - break; - } - } - if (num >= 3) { - /* - * Skip characters that cannot be used as size - * encoders. - */ - while (!is_size_encoder(num)) { - num--; - } - - *write_ptr++ = '*'; - *write_ptr++ = (unsigned char)(num + 29); - read_ptr += num; - ix += num; - } - } - *write_ptr = 0; - return write_ptr; -} - -/** - * @brief encode memory data using hexadecimal value of chars from '0' to 'f' - * - * For example, 0x3 (CTRL+C) will be encoded with hexadecimal values of - * '0' (0x30) and '3' (0x33): 0x3033. - * - * Use mem2hex() to encode a buffer avoid to send control chars that could - * perturbate communication protocol. - * - * @param data to encode - * @param output buffer - * @param size of data to encode - * @param Compress output data ? - * - * @return Pointer to the last char put in buf (null). - */ - -static unsigned char *mem2hex(unsigned char *mem, unsigned char *buf, - int count, int do_compress) -{ - int i; - unsigned char ch; - unsigned char *saved_buf = buf; - - for (i = 0; i < count; i++) { - ch = *mem++; - buf = put_hex_byte(buf, ch); - } - *buf = 0; - - if (do_compress) { - return compress(saved_buf); - } - - return buf; -} - -static inline int do_mem_probe(char *addr, int mode, int width, - int preserve, long *dummy) -{ - char *p = (char *)dummy; - - if (preserve) { - if (_mem_probe(addr, SYS_MEM_SAFE_READ, width, p) < 0) { - return -1; - } - } - if (_mem_probe(addr, mode, width, p) < 0) { - return -1; - } - - return 0; -} - -/** - * @brief Probe if memory location is valid - * - * @param addr Address to test - * @param mode Mode of access (SYS_MEM_SAFE_READ/WRITE) - * @param size Number of bytes to test - * @param width Width of memory access (1, 2, or 4) - * @param preserve Preserve memory on write test ? - * - * @return 0 if memory is accessible, -1 otherwise. - */ - -static int mem_probe(char *addr, int mode, int size, - int width, int preserve) -{ - long dummy; - - /* if memory length is zero, test is done */ - if (size == 0) { - return 0; - } - - /* Validate parameters */ - - preserve = mode == SYS_MEM_SAFE_READ ? 0 : preserve; - - if (width == 0) { - width = 1; - } else { - if ((width != 1) && (width != 2) && (width != 4)) { - return -1; - } - } - - /* Check addr, size & width parameters coherency */ - - if (((unsigned long)addr % width) || (size % width)) { - return -1; - } - - /* Check first address */ - - if (do_mem_probe(addr, mode, width, preserve, &dummy) < 0) { - return -1; - } - - /* Check if we have tested the whole memory */ - - if (width == size) { - return 0; - } - - /* Check last address */ - - addr = addr + size - width; - if (do_mem_probe(addr, mode, width, preserve, &dummy) < 0) { - return -1; - } - - return 0; -} - -static int put_packet(unsigned char *buffer) -{ - unsigned char checksum = 0; - int count = 0; - unsigned char ch; - - /* $#. */ - do { - put_debug_char('$'); - checksum = 0; - count = 0; - - /* Buffer terminated with null character */ - - while ((ch = buffer[count])) { - put_debug_char(ch); - checksum = (unsigned char)(checksum + ch); - count += 1; - } - - put_debug_char('#'); - put_debug_char(hex_chars[(checksum >> 4)]); - put_debug_char(hex_chars[(checksum & 0xf)]); - - if (!in_no_ack_mode) { - /* Wait for ack */ - - ch = get_debug_char(); - if (ch == '+') { - return 0; - } - if (ch == '$') { - put_debug_char('-'); - return 0; - } - if (ch == GDB_STOP_CHAR) { - cpu_stop_signal = GDB_SIG_INT; - gdb_debug_status = DEBUGGING; - post_event(); - return 0; - } - } else { - return 0; - } - } while (1); -} - -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS -/* - * Request a flush of pending notification packets. This is done by setting - * notif_pkt_pending to 1 before stopping the CPU. Once stopped, the - * control loop will send pending packets before resuming the system. - */ - -static void request_notification_packet_flush(void) -{ - /* - * Before stopping CPU we must indicate that we're stopping the system - * to handle a packet notification. During the packet notification, we - * should prevent CPU from reading protocol... - */ - - if (gdb_debug_status != NOT_DEBUGGING) { - return; - } - notif_pkt_pending = 1; - gdb_debug_status = DEBUGGING; - control_loop(); - gdb_debug_status = NOT_DEBUGGING; - RESUME_SYSTEM(); -} - -/* - * If the notification buffer for current CPU is full, or if we found a new - * line or carriage return character, then we must flush received data to - * remote client. - */ -static inline int must_flush_notification_buffer(unsigned char ch) -{ - return (notif_data_idx == GDB_NOTIF_DATA_SIZE) || - (ch == '\n') || (ch == '\r'); -} - -/* - * Write data to debug agent console. For performance reason, the data is - * bufferized until we receive a carriage return character or until the buffer - * gets full. - * - * The buffer is also automatically flushed when system is stopped. - */ -static u32_t write_to_console(char *buf, u32_t len) -{ - u32_t ix; - unsigned char ch; - - int key = irq_lock(); - - /* Copy data to notification buffer for current CPU */ - for (ix = 0; ix < len; ix++) { - ch = buf[ix]; - notif_data[notif_data_idx++] = ch; - - if (must_flush_notification_buffer(ch)) { - notif_data[notif_data_idx] = '\0'; - request_notification_packet_flush(); - } - } - - irq_unlock(key); - - return len; -} - -/* - * Handle pending notification packets for the CPU. It is invoked while - * running in GDB CPU control loop (When system is stopped). - */ -static void handle_notification(void) -{ - const char *name = CONFIG_GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR; - unsigned char checksum = 0; - int ix = 0; - unsigned char ch; - int more_data = 0; - u32_t max_packet_size; - u32_t data_size; - unsigned char *ptr = notif_data; - - /* First, check if there is pending data */ - - if (notif_data[0] == '\0') { - return; - } - -again: - /* - * Build notification packet. - * - * A notification packet has the form `%#', where data - * is the content of the notification, and checksum is a checksum of - * data, computed and formatted as for ordinary gdb packets. A - * notification's data never contains `$', `%' or `#' characters. Upon - * receiving a notification, the recipient sends no `+' or `-' to - * acknowledge the notification's receipt or to report its corruption. - * - * Every notification's data begins with a name, which contains no - * colon characters, followed by a colon character. - */ - put_debug_char('%'); - checksum = 0; - - /* Add name to notification packet */ - ix = 0; - while ((ch = name[ix++])) { - put_debug_char(ch); - checksum += ch; - } - - /* Name must be followed by a colon character. */ - put_debug_char(':'); - checksum += ':'; - - /* - * Add data to notification packet. - * Warning: The value to hex encoding double the size of the data, - * so we must not encode more than the remaining GDB buffer size - * divided by 2. - */ - max_packet_size = - GDB_BUF_SIZE - (strlen(name) + NOTIF_PACKET_OVERHEAD); - - data_size = strlen((char *)ptr); - if (data_size <= (max_packet_size / 2)) { - more_data = 0; - } else { - data_size = max_packet_size / 2; - more_data = 1; /* Not enough room in notif packet */ - } - - /* Encode data using hex values */ - for (ix = 0; ix < data_size; ix++) { - ch = hex_chars[(*ptr >> 4)]; - put_debug_char(ch); - checksum += ch; - ch = hex_chars[(*ptr & 0xf)]; - put_debug_char(ch); - checksum += ch; - ptr++; - } - - /* Terminate packet with # */ - put_debug_char('#'); - put_debug_char(hex_chars[(checksum >> 4)]); - put_debug_char(hex_chars[(checksum & 0xf)]); - - if (more_data) { - goto again; - } - - /* Clear buffer & index */ - notif_data[0] = '\0'; - notif_data_idx = 0; -} -#endif - -#ifdef GDB_ARCH_HAS_HW_BP -static int has_hit_a_hw_bp(void) -{ - /* instruction hw breakpoints are reported as sw breakpoints */ - return (cpu_stop_signal == GDB_SIG_TRAP) && - (cpu_stop_bp_type != GDB_SOFT_BP) && - (cpu_stop_bp_type != GDB_HW_INST_BP); -} -#endif - -static void do_post_event_hw_bp(unsigned char **buf, size_t *buf_size) -{ -#ifdef GDB_ARCH_HAS_HW_BP - /* - * If it's an hardware breakpoint, report the address and access - * type at the origin of the HW breakpoint. Supported syntaxes: - * watch: : Write access - * rwatch: : Read access - * awatch: : Read/Write Access - * Instruction hardware breakpoints are reported as software - * breakpoints - */ - - if (!has_hit_a_hw_bp()) { - return; - } - - int count = 0; - - switch (cpu_stop_bp_type) { - case GDB_HW_DATA_WRITE_BP: - count = snprintf((char *)*buf, *buf_size, ";watch"); - break; - - case GDB_HW_DATA_READ_BP: - count = snprintf((char *)*buf, *buf_size, ";rwatch"); - break; - - case GDB_HW_DATA_ACCESS_BP: - count = snprintf((char *)*buf, *buf_size, ";awatch"); - break; - } - if (count != 0) { - *buf += count; - *buf_size -= count; - count = snprintf((char *)*buf, *buf_size, ":%lx", - cpu_stop_hw_bp_addr); - *buf += count; - *buf_size -= count; - } - cpu_stop_hw_bp_addr = 0; - cpu_stop_bp_type = GDB_SOFT_BP; - cpu_stop_signal = GDB_SIG_NULL; -#endif -} - -static void write_regs_to_buffer(unsigned char **buf, size_t *buf_size) -{ - unsigned char *saved_buf; - int count; - -#ifdef GDB_ARCH_HAS_ALL_REGS - count = snprintf((char *)*buf, *buf_size, ";regs:"); - *buf += count; - *buf_size -= count; - saved_buf = *buf; - *buf = mem2hex(tmp_reg_buffer, *buf, sizeof(gdb_regs), 1); - *buf_size -= (*buf - saved_buf); -#else - int offset = 0; - int size = 4; - - count = snprintf((char *)*buf, *buf_size, ";%x:", GDB_PC_REG); - *buf += count; - *buf_size -= count; - gdb_arch_reg_info_get(GDB_PC_REG, &size, &offset); - saved_buf = *buf; - *buf = mem2hex(tmp_reg_buffer + offset, *buf, size, 1); - *buf_size -= (*buf - saved_buf); -#endif -} - -static void do_post_event(void) -{ - unsigned char *buf = gdb_buffer; - size_t buf_size = GDB_BUF_SIZE; - int count; - - if (buf != gdb_buffer) { - *buf++ = '|'; - buf_size--; - } - count = snprintf((char *)buf, buf_size, "T%02xthread:%02x", - cpu_stop_signal, 1); - buf += count; - buf_size -= count; - - do_post_event_hw_bp(&buf, &buf_size); - - if (valid_registers) { - gdb_arch_regs_get(&gdb_regs, (char *)tmp_reg_buffer); - write_regs_to_buffer(&buf, &buf_size); - } - - /* clear stop reason */ - cpu_stop_signal = GDB_SIG_NULL; - - *buf = '\0'; -} - -static void post_event(void) -{ - event_is_pending = 0; - - if (cpu_stop_signal != GDB_SIG_NULL) { - do_post_event(); - } else { - (void)snprintf((char *)gdb_buffer, GDB_BUF_SIZE, "S%02x", - GDB_SIG_INT); - } - - (void)put_packet(gdb_buffer); -} - -/** - * @brief Get a character from serial line - * - * It loops until it has received a character or until it has detected that a - * GDB event is pending and should be handled. - * - * Note that this routine should only be called from the gdb control loop when - * the system is stopped. - * - * @return -1 if no character has been received and there is a GDB event - * pending or debug operation pending, number of received character otherwise. - */ - -static int get_debug_char_raw(void) -{ - unsigned char ch; - - while (uart_poll_in(uart_console_dev, &ch) != 0) { - if (event_is_pending) { - return -1; - } - } - return ch; -} - -static unsigned char get_debug_char(void) -{ - return (unsigned char)get_debug_char_raw(); -} - -/** - * @brief Get a GDB serial packet - * - * Poll the serial line to get a full GDB serial packet. Once - * the packet is received, it computes its checksum and return acknowledgment. - * It then returns the packet to the caller. - * - * This routine must only be called when all CPUs are stopped (from the GDB - * CPU control loop). - * - * If a pending GDB event is detected or if a stop event is received from the - * client, the corresponding GDB stop event is sent to the client. This - * loop does also handle the GDB cpu loop hooks by the intermediate of - * get_debug_char() API. - * - * If a debug operation is pending, this routine returns immediately. - * - * @return Pointer to received packet or NULL on pending debug operation - */ - -static unsigned char *get_packet(unsigned char *buffer, size_t size) -{ - unsigned char checksum, c, *p; - - while (1) { - while ((c = get_debug_char()) != '$') { - if (!event_is_pending) { - return NULL; - } - - /* ignore other chars than GDB break character */ - if ((c == GDB_STOP_CHAR) || event_is_pending) { - post_event(); - } - } - - checksum = 0; - p = buffer; - - /* - * Continue reading characters until a '#' is found or until - * the end of the buffer is reached. - */ - - while (p < &buffer[size]) { - c = get_debug_char(); - - if (c == '#') { - break; - } else if (c == '$') { - /* start over */ - checksum = 0; - p = buffer; - continue; - } else { - checksum += c; - *p++ = c; - } - } - - *p = 0; - - if (c == '#') { - if (in_no_ack_mode) { - (void)get_debug_char(); - (void)get_debug_char(); - return buffer; - } - - unsigned char cs[2]; - - cs[0] = get_hex_char_value(get_debug_char()) << 4; - cs[1] = get_hex_char_value(get_debug_char()); - - if (checksum != (cs[0] | cs[1])) { - /* checksum failed */ - put_debug_char('-'); - } else { - /* checksum passed */ - put_debug_char('+'); - - if (buffer[2] == ':') { - put_debug_char(buffer[0]); - put_debug_char(buffer[1]); - return &buffer[3]; - } - return buffer; - } - } - } - - return NULL; -} - -/** - * @brief write a XML string into output buffer - * - * It takes care of offset, length and also deal with overflow (if the XML - * string is bigger than the output buffer). - */ - -static void write_xml_string(char *buf, const char *xml_str, int off, int len) -{ - size_t max_len = strlen(xml_str); - - if (off == max_len) { - strncat((char *)buf, "l", len - 1); - } else if (off > max_len) { - fill_output_buffer("E00"); - } else { - if ((off + max_len) <= len) { - /* we can read the full data */ - buf[0] = 'l'; - int size_to_copy = len <= (GDB_BUF_SIZE - 2) ? len : - GDB_BUF_SIZE - 2; - strncpy(&buf[1], xml_str + off, size_to_copy); - } else { - buf[0] = 'm'; - strncpy(&buf[1], xml_str + off, GDB_BUF_SIZE - 2); - buf[len + 1] = '\0'; - } - } -} - -/** - * @brief get XML target description - * - * This routine is used to build the string that will hold the XML target - * description provided to the GDB client. - * - * NOTE: Non-re-entrant, since it uses a static buffer. - * - * @return a pointer on XML target description - */ - -static char *get_xml_target_description(void) -{ - static char target_description[GDB_BUF_SIZE] = { 0 }; - char *ptr = target_description; - size_t buf_size = sizeof(target_description); - size_t size; - - if (target_description[0] != 0) { - return target_description; - } - - strncpy(ptr, xml_target_header, GDB_BUF_SIZE - 1); - size = strlen(ptr); - ptr += size; - buf_size -= size; - - /* Add architecture definition */ - - (void)snprintf(ptr, buf_size, " %s\n", - GDB_TGT_ARCH); - size = strlen(ptr); - ptr += size; - buf_size -= size; - - strncpy(ptr, xml_target_footer, - GDB_BUF_SIZE - (ptr - target_description) - 1); - - return target_description; -} - -/* utility functions for handling each case of protocal_parse() */ - -static void handle_new_connection(void) -{ - /* - * This is a new connection. Clear in_no_ack_mode field if it was set - * and send acknowledgment for this command that has not been sent as - * it should have. - */ - if (in_no_ack_mode) { - put_debug_char('+'); - in_no_ack_mode = 0; - } - - snprintf((char *)gdb_buffer, GDB_BUF_SIZE, "T02thread:%02x;", 1); - - /* - * This is an initial connection, should remove all - * the breakpoints and cleanup. - */ - REMOVE_ALL_INSTALLED_BREAKPOINTS(); - client_is_connected = 1; -} - -static void reboot(void) -{ -#ifdef CONFIG_REBOOT - sys_reboot(SYS_REBOOT_COLD); - fill_output_buffer(STUB_OK); -#endif -} - -static void detach(void) -{ - fill_output_buffer(STUB_OK); - REMOVE_ALL_INSTALLED_BREAKPOINTS(); - client_is_connected = 0; - gdb_debug_status = NOT_DEBUGGING; - RESUME_SYSTEM(); - in_no_ack_mode = 0; -} - -static unsigned char *handle_thread_query(unsigned char *packet) -{ - int thread; - - if (!hex_str_to_int(&packet, &thread)) { - gdb_buffer[0] = '\0'; - return packet; - } - if (thread != 1) { - fill_output_buffer(STUB_ERROR); - } else { - fill_output_buffer(STUB_OK); - } - - return packet; -} - -#ifdef CONFIG_REBOOT -#define STR_REBOOT ";reboot+" -static size_t concat_reboot_feature_if_supported(size_t size) -{ - strncat((char *)gdb_buffer, STR_REBOOT, size); - return sizeof(STR_REBOOT); -} -#else -#define concat_reboot_feature_if_supported(size) (0) -#endif - -static ALWAYS_INLINE int is_valid_xml_query(unsigned char **packet, - int *off, int *len) -{ - unsigned char *p = *packet; - int is_valid = hex_str_to_int(&p, off) && *p++ == ',' - && hex_str_to_int(&p, len) && *p == '\0'; - - *packet = p; - return is_valid; -} - -static unsigned char *handle_xml_query(unsigned char *packet) -{ - int off, len; - - packet += 11; - if (is_valid_xml_query(&packet, &off, &len)) { - char *xml = get_xml_target_description(); - - write_xml_string((char *)gdb_buffer, xml, off, len); - } else { - fill_output_buffer(STUB_ERROR); - } - - return packet; -} - -static const char *supported_features_cmd = - "PacketSize=%x;qXfer:features:read+;QStartNoAckMode+" -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS - ";" CONFIG_GDB_REMOTE_SERIAL_EXT_NOTIF_PREFIX_STR "+" -#endif -; - -static unsigned char *handle_general_query(unsigned char *packet) -{ - if (packet[0] == 'C') { - snprintf((char *)gdb_buffer, GDB_BUF_SIZE, "QC%x", 1); - } else if (strncmp((char *)packet, "wr.", 3) == 0) { - packet += 3; - gdb_buffer[0] = '\0'; - } else if (strcmp((char *)packet, "Supported") == 0) { - size_t size = GDB_BUF_SIZE; - - snprintf((char *)gdb_buffer, size, supported_features_cmd, - GDB_BUF_SIZE); - size -= (strlen((char *)gdb_buffer) + 1); - - size -= concat_reboot_feature_if_supported(size); - - strncat((char *)gdb_buffer, STR_TYPE, size); - size -= sizeof(STR_TYPE); - } else if (strncmp((char *)packet, "Xfer:features:read:", 19) == 0) { - packet += 19; - if (strncmp((char *)packet, "target.xml:", 11) == 0) { - packet = handle_xml_query(packet); - } else { - gdb_buffer[0] = '\0'; - } - } else { - gdb_buffer[0] = '\0'; - } - - return packet; -} - -static ALWAYS_INLINE void handle_get_registers(void) -{ - if (!valid_registers) { - fill_output_buffer("E02"); - return; - } - (void)gdb_arch_regs_get(&gdb_regs, (char *)tmp_reg_buffer); - mem2hex(tmp_reg_buffer, gdb_buffer, GDB_NUM_REG_BYTES, 1); -} - -static ALWAYS_INLINE -unsigned char *handle_write_registers(unsigned char *packet) -{ - if (!valid_registers) { - fill_output_buffer("E02"); - return packet; - } - - (void)gdb_arch_regs_get(&gdb_regs, (char *)tmp_reg_buffer); - - for (int i = 0; i < GDB_NUM_REG_BYTES; i++) { - int value = hex_str_to_byte(&packet); - - if (value < 0) { - break; - } - tmp_reg_buffer[i] = (unsigned char)value; - } - - gdb_arch_regs_set(&gdb_regs, (char *)tmp_reg_buffer); - - fill_output_buffer(STUB_OK); - return packet; -} - -#ifdef GDB_HAS_SINGLE_REG_ACCESS -static ALWAYS_INLINE -unsigned char *handle_write_single_register(unsigned char *packet) -{ - int reg_num = 0; - int offset = 0; - int size = 4; - int i, value; - - if (!valid_registers) { - fill_output_buffer("E02"); - return packet; - } - if (!hex_str_to_int(&packet, ®_num) || *(packet++) != '=') { - fill_output_buffer("E02"); - return packet; - } - - gdb_arch_regs_get(&gdb_regs, (char *)tmp_reg_buffer); - gdb_arch_reg_info_get(reg_num, &size, &offset); - - for (i = 0; i < size; i++) { - value = hex_str_to_byte(&packet); - if (value < 0) { - break; - } - tmp_reg_buffer[offset + i] = (unsigned char)value; - } - if (i != size) { - fill_output_buffer(STUB_ERROR); - return packet; - } - gdb_arch_regs_set(&gdb_regs, (char *)tmp_reg_buffer); - fill_output_buffer(STUB_OK); - return packet; -} - -static ALWAYS_INLINE -unsigned char *handle_read_single_register(unsigned char *packet) -{ - int reg_num = 0; - int offset = 0; - int size = 4; - - if (!valid_registers) { - fill_output_buffer("E02"); - return packet; - } - /* p */ - - if (!hex_str_to_int(&packet, ®_num)) { - fill_output_buffer("E02"); - return packet; - } - - gdb_arch_regs_get(&gdb_regs, (char *)tmp_reg_buffer); - gdb_arch_reg_info_get(reg_num, &size, &offset); - mem2hex(tmp_reg_buffer + offset, gdb_buffer, size, 1); - return packet; -} -#endif - -static ALWAYS_INLINE -unsigned char *handle_read_memory(unsigned char *packet) -{ - /* m, */ - - long long addr; - int length; - void *p; - - if (hex_str_to_longlong((unsigned char **)&packet, &addr) == 0) { - fill_output_buffer("E01"); - return packet; - } - - if (!(*packet++ == ',' && hex_str_to_int(&packet, &length))) { - fill_output_buffer("E01"); - return packet; - } - - p = (void *)((long)addr); - if (mem_probe(p, SYS_MEM_SAFE_READ, length, 0, 1) == -1) { - /* No read access */ - fill_output_buffer("E01"); - return packet; - } - - /* Now read memory */ - mem2hex(p, gdb_buffer, length, 1); - return packet; -} - -#define WRITE_MEM_SIG(x) \ - unsigned char *(x)(unsigned char *packet, unsigned char *dest, int len) -typedef WRITE_MEM_SIG (write_mem_t); - -static ALWAYS_INLINE unsigned char *handle_write_memory(unsigned char *packet, - write_mem_t *write_mem) -{ - long long addr; - int len; - unsigned char *p; - - /* [X or P],:... */ - - if (hex_str_to_longlong(&packet, &addr) == 0) { - fill_output_buffer("E02"); - return packet; - } - - p = packet; /* to allow the if expression to fit on one line */ - if (!(*p++ == ',' && hex_str_to_int(&p, &len) && *p++ == ':')) { - fill_output_buffer("E02"); - return p; - } - packet = p; - - p = (void *)((long)addr); - if (mem_probe((char *) p, SYS_MEM_SAFE_WRITE, len, 0, 1) == -1) { - /* No write access */ - fill_output_buffer("E02"); - return packet; - } - - packet = write_mem(packet, p, len); - - fill_output_buffer(STUB_OK); - return packet; -} - -static WRITE_MEM_SIG(write_memory) -{ - unsigned char value; - int i; - - for (i = 0; i < len; i++) { - value = hex_str_to_byte(&packet); - if (value < 0) { - break; - } - dest[i] = (unsigned char)value; - } - - return packet; -} - -static WRITE_MEM_SIG(write_memory_from_binary_format) -{ - unsigned char value; - int i; - - for (i = 0; i < len; i++) { - value = packet[0]; - packet++; - if (value == '}') { - value = packet[0] ^ 0x20; - packet++; - } - dest[i] = (unsigned char)value; - } - - return packet; -} - -static ALWAYS_INLINE -unsigned char *handle_pass_signal_to_context(unsigned char *packet) -{ - int signal; - - /* read signal number */ - if (!hex_str_to_int(&packet, &signal)) { - fill_output_buffer("E02"); - return packet; - } - - cpu_pending_signal = signal; - - if (*packet == ';') { - packet++; - } - - return packet; -} - -static ALWAYS_INLINE -unsigned char *handle_continue_execution(unsigned char *packet) -{ - long long addr; - - /* try to read optional parameter, PC unchanged if no param */ - hex_str_to_longlong(&packet, &addr); - gdb_debug_status = NOT_DEBUGGING; - - return packet; -} - -static ALWAYS_INLINE unsigned char *handle_step(unsigned char *packet) -{ - long long addr; - - /* try to read optional parameter, PC unchanged if no param */ - hex_str_to_longlong(&packet, &addr); - - gdb_debug_status = SINGLE_STEP; - - return packet; -} - -static ALWAYS_INLINE -unsigned char *handle_vcont_action(unsigned char *packet, int *do_not_send_ack) -{ - char action; - int signal = 0, thread; - - packet += 5; - action = *packet++; - if ((action != 'c') && (action != 'C') && - (action != 's') && (action != 'S')) { - gdb_buffer[0] = '\0'; - return packet; - } - - if ((action == 'C') || (action == 'S')) { - /* read signal number */ - if (!hex_str_to_int(&packet, &signal)) { - fill_output_buffer("E02"); - return packet; - } - } - - if (*packet == ':') { - packet++; - hex_str_to_int(&packet, &thread); - } - - if (signal != 0) { - cpu_pending_signal = signal; - } - - if ((action == 'c') || (action == 'C')) { - gdb_debug_status = NOT_DEBUGGING; - } else { - gdb_debug_status = SINGLE_STEP; - } - - *do_not_send_ack = 1; - return packet; -} - -#ifdef GDB_ARCH_HAS_RUNCONTROL -static ALWAYS_INLINE -unsigned char *handle_breakpoint_install(unsigned char *packet, - add_del_bp_t *bp_op) -{ - /* remove (ztype,addr,length) or insert (Ztype,addr,length) */ - - int type, len; - long long addr; - enum gdb_error_code err; - - /* read & */ - if (!(hex_str_to_int(&packet, &type) && *packet++ == ',' && - hex_str_to_longlong(&packet, &addr))) { - fill_output_buffer("E07"); - return packet; - } - - /* read length */ - if (!(*packet++ == ',' && hex_str_to_int(&packet, &len))) { - fill_output_buffer("E07"); - return packet; - } - - if (bp_op(type, (long)addr, len, &err) == 0) { - fill_output_buffer(STUB_OK); - } else { - snprintf((char *)gdb_buffer, GDB_BUF_SIZE, "E%02d", err); - } - - return packet; -} -#endif - -/** - * @brief parse given GDB command string - * - * Parse and execute the given GDB command string, and send acknowledgment if - * acknowledgment is enabled. - * - * @return 0 on success, -1 if failed to send acknowledgment. - */ - -static int protocol_parse(unsigned char *packet) -{ - unsigned char ch; - int do_not_send_ack = 0; - - ch = *packet++; - - switch (ch) { - case '?': - handle_new_connection(); - break; - case 'k': - /* Kill request: we use it to reboot */ - reboot(); - break; - case 'D': - detach(); - break; - case 'T': - packet = handle_thread_query(packet); - break; - case 'Q': - /* the only 'Q' command we support is "start no-ack mode" */ - if (strcmp((const char *)packet, "StartNoAckMode") == 0) { - in_no_ack_mode = 1; - fill_output_buffer(STUB_OK); - } else { - gdb_buffer[0] = '\0'; - } - break; - case 'q': - packet = handle_general_query(packet); - break; - case 'g': - handle_get_registers(); - break; - case 'G': - packet = handle_write_registers(packet); - break; - -#ifdef GDB_HAS_SINGLE_REG_ACCESS - case 'P': - packet = handle_write_single_register(packet); - break; - case 'p': - packet = handle_read_single_register(packet); - break; -#endif - - case 'm': - packet = handle_read_memory(packet); - break; - case 'M': - packet = handle_write_memory(packet, write_memory); - break; - case 'X': - packet = handle_write_memory(packet, - write_memory_from_binary_format); - break; - - case 'C': - packet = handle_pass_signal_to_context(packet); - /* fall through */ - case 'c': - packet = handle_continue_execution(packet); - do_not_send_ack = 1; - break; - - case 'S': - packet = handle_pass_signal_to_context(packet); - /* fall through */ - case 's': - packet = handle_step(packet); - do_not_send_ack = 1; - break; - case 'v': - if (strcmp((const char *)packet, "Cont?") == 0) { - fill_output_buffer("vCont;c;s;C;S"); - break; - } else if (strncmp((const char *)packet, "Cont;", 5) != 0) { - gdb_buffer[0] = '\0'; - break; - } - - packet = handle_vcont_action(packet, &do_not_send_ack); - break; - -#ifdef GDB_ARCH_HAS_RUNCONTROL - case 'z': - packet = handle_breakpoint_install(packet, delete_bp); - break; - case 'Z': - packet = handle_breakpoint_install(packet, add_bp); - break; -#endif - default: - /* in case of an unsupported command, send empty response */ - gdb_buffer[0] = '\0'; - break; - } - - /* Send the acknowledgment command when necessary */ - - if (!do_not_send_ack) { - if (put_packet(gdb_buffer) < 0) { - return -1; - } - } - - return 0; -} - -/* - * function: put_debug_char - * description: - * - "What you must do for the stub" - * - Write a single character from a port. - */ -static void put_debug_char(unsigned char ch) -{ - (void)uart_poll_out(uart_console_dev, ch); -} - -#ifdef GDB_ARCH_HAS_RUNCONTROL - -/** - * @brief add an hardware breakpoint to debug registers set - * - * This routine adds an hardware breakpoint to debug registers structure. - * It does not update the debug registers. - * - * @param addr Address where to set the breakpoint - * @param type Type of breakpoint - * @param len Length of data - * @param err Container for returning error code - * - * @return 0 on success, -1 if failed (Error code returned via @a err). - */ - -static int add_hw_bp(long addr, enum gdb_bp_type type, int len, - enum gdb_error_code *err) -{ -#ifdef GDB_ARCH_HAS_HW_BP - if (gdb_hw_bp_set(&dbg_regs, addr, type, len, err) == -1) { - return -1; - } - - hw_bp_cnt++; - return 0; -#else - *err = GDB_ERROR_HW_BP_NOT_SUP; - return -1; -#endif -} - -/** - * @brief remove an hardware breakpoint from debug registers set - * - * This routine removes an hardware breakpoint from debug registers structure. - * It does not update the debug registers. - * - * @param addr Address where to set the breakpoint - * @param type Type of breakpoint - * @param len Length of data - * @param err Container for returning error code - * - * @return 0 on success, -1 if failed (Error code returned via @a err). - */ - -static int remove_hw_bp(long addr, enum gdb_bp_type type, int len, - enum gdb_error_code *err) -{ -#ifdef GDB_ARCH_HAS_HW_BP - if (gdb_hw_bp_clear(&dbg_regs, addr, type, len, err) == -1) { - return -1; - } - - hw_bp_cnt--; - return 0; -#else - *err = GDB_ERROR_HW_BP_NOT_SUP; - return -1; -#endif -} - -/** - * @brief add a new breakpoint or watchpoint to breakpoint list - * - * This routine adds a new breakpoint or watchpoint to breakpoint list. For - * watchpoints, this routine checks that the given type/length combination is - * supported on current architecture, and that debug registers are not full. - * - * @param type GDB breakpoint type: - * - * 0 : software breakpoint (GDB_SOFT_BP) - * 1 : hardware breakpoint (GDB_HW_INST_BP) - * 2 : write watchpoint (GDB_HW_DATA_WRITE_BP) - * 3 : read watchpoint (GDB_HW_DATA_READ_BP) - * 4 : access watchpoint (GDB_HW_DATA_ACCESS_BP) - * - * @param addr Breakpoint address - * @param len For a software breakpoint, len specifies the size of the - * instruction to be patched. For hardware breakpoints and - * watchpoints length specifies the memory region to be monitored. - * @param err Pointer to error code if failed to add breakpoint. - * - * @return 0 on success, -1 if failed to add breakpoint. - */ - -static int add_bp(enum gdb_bp_type type, long addr, int len, - enum gdb_error_code *err) -{ - if (type != GDB_SOFT_BP) { - return add_hw_bp(addr, type, len, err); - } - - if (mem_probe((void *)addr, SYS_MEM_SAFE_READ, len, 0, 1) == -1) { - *err = GDB_ERROR_INVALID_MEM; - return -1; - } - - /* Add software breakpoint to BP list */ - - for (int ix = 0; ix < MAX_SW_BP; ix++) { - if (bp_array[ix].valid == 0) { - bp_array[ix].valid = 1; - bp_array[ix].enabled = 0; - bp_array[ix].addr = (gdb_instr_t *)addr; - return 0; - } - } - - *err = GDB_ERROR_BP_LIST_FULL; - return -1; -} - -/** - * @brief delete a breakpoint or watchpoint from breakpoint list - * - * @return 0 on success, -1 if failed to remove breakpoint. - */ - -static int delete_bp(enum gdb_bp_type type, long addr, int len, - enum gdb_error_code *err) -{ - gdb_instr_t *bp_addr = (gdb_instr_t *)addr; - - if (type != GDB_SOFT_BP) { - return remove_hw_bp(addr, type, len, err); - } - - for (int ix = 0; ix < MAX_SW_BP; ix++) { - if (bp_array[ix].valid && bp_array[ix].addr == bp_addr) { - - bp_array[ix].valid = 0; - - /* - * Make sure all valid entries are contiguous to speed - * up breakpoint table parsing. - */ - - for (int jx = ix + 1; jx < MAX_SW_BP; jx++) { - if (bp_array[jx].valid == 1) { - bp_array[jx - 1] = bp_array[jx]; - bp_array[jx].valid = 0; - } else { - break; - } - } - - return 0; - - } else if (!bp_array[ix].valid) { - break; - } - } - - *err = GDB_ERROR_INVALID_BP; - return -1; -} - -static void remove_all_installed_breakpoints(void) -{ - for (int ix = 0; ix < MAX_SW_BP; ix++) { - if (!bp_array[ix].valid) { - break; - } - bp_array[ix].valid = 0; - } -} - -#ifdef GDB_ARCH_HAS_HW_BP -static inline void set_debug_regs_for_hw_breakpoints(void) -{ - if (hw_bp_cnt > 0) { - gdb_dbg_regs_set(&dbg_regs); - } -} -#else -#define set_debug_regs_for_hw_breakpoints() do { } while ((0)) -#endif - -/* - * Physically install breakpoints, and make sure that modified memory is - * flushed on all CPUs. - * - * Must only be called when ready to exit the CPU control loop. - */ - -static void install_breakpoints(void) -{ - gdb_instr_t instr = GDB_BREAK_INSTRUCTION; - - /* Software breakpoints installation */ - - for (int ix = 0; ix < MAX_SW_BP; ix++) { - if (bp_array[ix].valid && !bp_array[ix].enabled) { - gdb_instr_t *addr = bp_array[ix].addr; - - bp_array[ix].instr = *addr; - (void)set_instruction(addr, &instr, sizeof(instr)); - bp_array[ix].enabled = 1; - } else if (!bp_array[ix].valid) { - break; - } - } - - set_debug_regs_for_hw_breakpoints(); -} - - -#ifdef GDB_ARCH_HAS_HW_BP -static inline void clear_debug_regs_for_hw_breakpoints(void) -{ - if (hw_bp_cnt > 0) { - gdb_dbg_regs_clear(); - } -} -#else -#define clear_debug_regs_for_hw_breakpoints() do { } while ((0)) -#endif - -/* - * Physically uninstall breakpoints, and make sure that modified memory is - * flushed on all CPUs. - * - * Must only be called in the CPU control loop. - */ - -static void uninstall_breakpoints(void) -{ - for (int ix = 0; ix < MAX_SW_BP; ix++) { - if (bp_array[ix].valid == 1 && bp_array[ix].enabled) { - gdb_instr_t *addr = bp_array[ix].addr; - (void)set_instruction(addr, &bp_array[ix].instr, - sizeof(gdb_instr_t)); - bp_array[ix].enabled = 0; - } else if (bp_array[ix].valid == 0) { - break; - } - } - - clear_debug_regs_for_hw_breakpoints(); -} - -/* Re-install breakpoints and resume the system. */ - -static void resume_system(void) -{ - /* - * System must not be resumed if we're going to execute a single step. - */ - - if (gdb_debug_status == SINGLE_STEP) { - return; - } - - install_breakpoints(); -} - -static inline void enter_trace_mode(void) -{ -#ifdef GDB_ARCH_NO_SINGLE_STEP - gdb_instr_t bp_instr = GDB_BREAK_INSTRUCTION; - - gdb_step_emu_next_pc = gdb_get_next_pc(&gdb_regs); - gdb_step_emu_instr = *gdb_step_emu_next_pc; - (void)set_instruction(gdb_step_emu_next_pc, &bp_instr, - sizeof(gdb_instr_t)); - trace_lock_key = gdb_int_regs_lock(&gdb_regs); -#else - /* Handle single step request for runcontrol CPU */ - - trace_lock_key = gdb_trace_mode_set(&gdb_regs); -#endif -} -static inline void disable_trace_mode(void) -{ -#ifdef GDB_ARCH_NO_SINGLE_STEP - /* remove temporary breakpoint */ - (void)set_instruction(gdb_step_emu_next_pc, - &gdb_step_emu_instr, - sizeof(gdb_instr_t)); - /* Disable trace mode */ - gdb_int_regs_unlock(&gdb_regs, trace_lock_key); -#else - /* Disable trace mode */ - gdb_trace_mode_clear(&gdb_regs, trace_lock_key); -#endif -} - -/** - * @brief stop mode agent BP/trace handler - * - * Common handler of breakpoint and trace mode exceptions. - * It is invoked with interrupts locked. - * - * @return n/a - */ - -void gdb_handler(enum gdb_exc_mode mode, void *esf, int signal) -{ - /* Save BP/Trace handler registers */ - gdb_arch_regs_from_esf(&gdb_regs, (NANO_ESF *)esf); - valid_registers = 1; - - if (mode == GDB_EXC_TRACE) { - /* Check if GDB did request a step */ - if (gdb_debug_status != SINGLE_STEP) { - return; - } - - /* No longer pending trace mode exception */ - gdb_debug_status = DEBUGGING; - - disable_trace_mode(); - } - - event_is_pending = 1; - cpu_stop_signal = signal; - - /* Enter stop mode agent control loop */ - control_loop(); - - /* Restore BP handler registers */ - gdb_arch_regs_to_esf(&gdb_regs, (NANO_ESF *)esf); - - /* Resume system if not handling a single step */ - RESUME_SYSTEM(); -} - -static int set_instruction(void *addr, gdb_instr_t *instr, size_t size) -{ - - if (_mem_safe_write_to_text_section(addr, (char *)instr, size) < 0) { - return -EFAULT; - } - sys_cache_flush((vaddr_t) addr, size); - return 0; -} - -#endif /* GDB_ARCH_HAS_RUNCONTROL */ - -static inline void setup_singlestep_if_non_steppable_instruction(void) -{ -#ifdef GDB_ARCH_CAN_STEP - if (gdb_debug_status == SINGLE_STEP) { - if (!GDB_ARCH_CAN_STEP(&gdb_regs)) { - gdb_debug_status = DEBUGGING; - event_is_pending = 1; - cpu_stop_signal = GDB_SIG_TRAP; - } - } -#endif -} - -static inline int handle_single_stepping(void) -{ -#ifdef GDB_ARCH_HAS_RUNCONTROL - setup_singlestep_if_non_steppable_instruction(); - - if (gdb_debug_status == SINGLE_STEP) { - enter_trace_mode(); - return 1; - } -#endif - return 0; -} - -/** - * @brief GDB control loop - * - * The CPU control loop is an active wait loop used to stop CPU activity. - * - * It must be called with interrupts locked. - * - * It loops while waiting for debug events which can be: - * - * - System resumed: gdb_debug_status != NOT_DEBUGGING - * The control loop must be exited. - * - * - Single step request: gdb_debug_status == SINGLE_STEP - * Notify client that CPU is already stopped. - * This is done by setting event_is_pending = 1. - * event_is_pending will be handled by next get_packet(). - * - * @return n/a - */ - -static void control_loop(void) -{ - unsigned char ch; - - UNINSTALL_BREAKPOINTS(); - - /* Flush input buffer */ - while (uart_poll_in(uart_console_dev, &ch) == 0) { - if (ch == GDB_STOP_CHAR) { - gdb_debug_status = DEBUGGING; - cpu_stop_signal = GDB_SIG_INT; - event_is_pending = 1; - break; - } - } - - while (gdb_debug_status != NOT_DEBUGGING) { -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS - /* - * Check if system has been stopped to handle a notification - * packet: If a notification is pending (notif_pkt_pending), - * but no stop signal has been set. - */ - if ((cpu_stop_signal == GDB_SIG_NULL) && notif_pkt_pending) { - handle_notification(); - /* Mark packet notification as done */ - notif_pkt_pending = 0; - break; - } -#endif - - unsigned char *packet = get_packet(gdb_buffer, GDB_BUF_SIZE); - - if (packet) { - protocol_parse(packet); - } - - if (handle_single_stepping()) { - return; - } - } -} - -/** - * @brief handle a system stop request - * - * The purpose of this routine is to handle a stop request issued by remote - * debug client. It is called when receiving a break char. - * - * It indicates that a GDB event is pending (the answer to stop request) and - * transfer control from the runtime system to the stop mode agent. The event - * will be posted by this control loop. - * - * @return n/a - */ - -static void handle_system_stop(NANO_ISF *regs, int signal) -{ - int key = irq_lock(); - - gdb_debug_status = DEBUGGING; - if (signal != 0) { - cpu_stop_signal = signal; - } else { - cpu_stop_signal = GDB_SIG_INT; /* Stopped by a command */ - } - - /* Save registers */ - if (regs == GDB_INVALID_REG_SET) { - valid_registers = 0; - } else { - if (!regs) { - regs = sys_debug_current_isf_get(); - } - gdb_arch_regs_from_isf(&gdb_regs, regs); - valid_registers = 1; - } - - /* A GDB event is pending */ - event_is_pending = 1; - - /* Transfer control to the control loop */ - control_loop(); - - /* Load registers */ - if (valid_registers) { - gdb_arch_regs_to_isf(&gdb_regs, regs); - } - - /* Resume system if not a single step request */ - RESUME_SYSTEM(); - - irq_unlock(key); -} - -/** - * @brief wrapper to send a character to console - * - * This routine is a specific wrapper to send a character to console. - * If the GDB Server is started, this routine intercepts the data and transfer - * it to the connected debug clients using a GDB notification packet. - * - * @return n/a - */ - -static UART_CONSOLE_OUT_DEBUG_HOOK_SIG(gdb_console_out) -{ -#ifdef GDB_ARCH_HAS_REMOTE_SERIAL_EXT_USING_NOTIF_PACKETS - /* - * If remote debug client is connected, then transfer data to remote - * client. Otherwise, discard this character. - */ - if (client_is_connected) { - write_to_console(&c, 1); - return UART_CONSOLE_DEBUG_HOOK_HANDLED; - } -#endif - return !UART_CONSOLE_DEBUG_HOOK_HANDLED; -} - -#ifdef CONFIG_GDB_SERVER_INTERRUPT_DRIVEN -static int console_irq_input_hook(u8_t ch) -{ - if (ch == GDB_STOP_CHAR) { - (void)irq_lock(); - - handle_system_stop(NULL, 0); - return 1; - } - return 0; -} -#endif - -void system_stop_here(void *regs) -{ - int key = irq_lock(); - - handle_system_stop((NANO_ISF *) regs, GDB_SIG_STOP); - - irq_unlock(key); -} - -void _debug_fatal_hook(const NANO_ESF *esf) -{ - struct gdb_reg_set regs; - - gdb_arch_regs_from_esf(®s, (NANO_ESF *) esf); - system_stop_here((void *)®s); - gdb_arch_regs_to_esf(®s, (NANO_ESF *) esf); -} - -#ifdef CONFIG_GDB_SERVER_INTERRUPT_DRIVEN -static void init_interrupt_handling(void) -{ - k_fifo_init(&cmds_queue); - k_fifo_init(&avail_queue); - uart_console_in_debug_hook_install(console_irq_input_hook); - uart_register_input(&avail_queue, &cmds_queue, NULL); -} -#else -#define init_interrupt_handling() do { } while ((0)) -#endif - -#ifdef CONFIG_MEM_SAFE_NUM_EXTRA_REGIONS -static void init_mem_safe_access(void) -{ - (void)_mem_safe_region_add((void *)CONFIG_GDB_RAM_ADDRESS, - GDB_RAM_SIZE, SYS_MEM_SAFE_READ); - (void)_mem_safe_region_add((void *)CONFIG_GDB_RAM_ADDRESS, - GDB_RAM_SIZE, SYS_MEM_SAFE_WRITE); -} -#else -#define init_mem_safe_access() do { } while ((0)) -#endif - -static int init_gdb_server(struct device *unused) -{ - static int gdb_is_initialized; - - if (gdb_is_initialized) { - return -1; - } - - gdb_arch_init(); - - uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); - - uart_console_out_debug_hook_install(gdb_console_out); - - init_interrupt_handling(); - init_mem_safe_access(); - - gdb_is_initialized = 1; - system_stop_here(GDB_INVALID_REG_SET); - - return 0; -} - -SYS_INIT(init_gdb_server, POST_KERNEL, 1); diff --git a/subsys/debug/mem_safe_check_boundaries.c b/subsys/debug/mem_safe_check_boundaries.c deleted file mode 100644 index 2fb9434f388..00000000000 --- a/subsys/debug/mem_safe_check_boundaries.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file safe memory access routines, software implementation that verifies - * accesses are within memory region boundaries. - * - * @details See debug/Kconfig and the "Safe memory access" help for details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define NUM_REGIONS (CONFIG_MEM_SAFE_NUM_EXTRA_REGIONS + 2) - -/* - * The table of regions has the RO regions at the bottom and the RW regions at - * the top, and regions are added by moving the ro_end/rw_end pointers towards - * each other. The table is full when the pointers cross, i.e. when ro_end > - * rw_end. - */ -struct { - vaddr_t addr; - vaddr_t last_byte; -} mem_regions[NUM_REGIONS]; - -#define ro_base 0 -#define rw_base (NUM_REGIONS - 1) - -static int ro_end = ro_base; -static int rw_end = rw_base; - -#define IMAGE_ROM_START ((vaddr_t)&_image_rom_start) -#define IMAGE_ROM_END ((vaddr_t)&_image_rom_end) - -#define IMAGE_RAM_START ((vaddr_t)&_image_ram_start) -#define IMAGE_RAM_END ((vaddr_t)&_image_ram_end) - -#define IMAGE_TEXT_START ((vaddr_t)&_image_text_start) -#define IMAGE_TEXT_END ((vaddr_t)&_image_text_end) - -#define VALID_PERMISSION_MASK 0x00000001 /* permissions use only the lsb */ - -static inline void write_to_mem(void *dest, void *src, int width) -{ - switch (width) { - case 4: - *((vaddr_t *)dest) = *((const vaddr_t *)src); - break; - case 2: - *((u16_t *)dest) = *((const u16_t *)src); - break; - case 1: - *((char *)dest) = *((const char *)src); - break; - } -} - -static inline int is_in_region(int slot, vaddr_t addr, vaddr_t end_addr) -{ - vaddr_t region_start = mem_regions[slot].addr; - vaddr_t region_last_byte = mem_regions[slot].last_byte; - - return addr >= region_start && end_addr <= region_last_byte; -} - -static inline int is_in_a_ro_region(vaddr_t addr, vaddr_t end_addr) -{ - int slot = ro_base; - - while (slot < ro_end && slot <= rw_end) { - if (is_in_region(slot, addr, end_addr)) { - return 1; - } - ++slot; - } - - return 0; -} - -static inline int is_in_a_rw_region(vaddr_t addr, vaddr_t end_addr) -{ - int slot = rw_base; - - while (slot > rw_end && slot >= ro_end) { - if (is_in_region(slot, addr, end_addr)) { - return 1; - } - --slot; - } - - return 0; -} - -static inline int mem_probe_no_check(void *p, int perm, size_t num_bytes, - void *buf) -{ - vaddr_t addr = (vaddr_t)p; - vaddr_t end_addr = addr + num_bytes - 1; - - int is_in_rw = is_in_a_rw_region(addr, end_addr); - int is_in_ro = is_in_a_ro_region(addr, end_addr); - - int valid_mem; - void *src, *dest; - - if (perm == SYS_MEM_SAFE_READ) { - dest = buf; src = p; - valid_mem = is_in_rw || is_in_ro; - } else { - dest = p; src = buf; - valid_mem = is_in_rw; - } - - if (likely(valid_mem)) { - write_to_mem(dest, src, num_bytes); - return 0; - } - return -EFAULT; -} - -static inline int is_perm_valid(int perm) -{ - return !(perm & ~VALID_PERMISSION_MASK); -} - -static inline int is_num_bytes_valid(size_t num_bytes) -{ - return is_power_of_two(num_bytes) && num_bytes <= sizeof(vaddr_t); -} - -int _mem_probe(void *p, int perm, size_t num_bytes, void *buf) -{ - if (unlikely(!is_perm_valid(perm))) { - return -EINVAL; - } - - if (unlikely(!is_num_bytes_valid(num_bytes))) { - return -EINVAL; - } - - return mem_probe_no_check(p, perm, num_bytes, buf); -} - -static inline int mem_access(void *p, void *buf, size_t num_bytes, - int len, int perm) -{ - char *p_char = p, *buf_char = buf, *p_end = ((char *)p + len); - - while (p_char < p_end) { - int error = mem_probe_no_check(p_char, perm, num_bytes, buf_char); - - if (unlikely(error < 0)) { - return error; - } - p_char += num_bytes; - buf_char += num_bytes; - } - - return 0; -} - -static inline int get_align(const u32_t value) -{ - return (value & 1) ? 1 : (value & 2) ? 2 : 4; -} - -static inline int get_width(const void *p1, const void *p2, - size_t num_bytes, int width) -{ - vaddr_t p1_addr = (vaddr_t)p1, p2_addr = (vaddr_t)p2; - - if (width == 0) { - u32_t align_check = num_bytes | p1_addr | p2_addr; - - return get_align(align_check); - } - - if (unlikely(p1_addr & (width - 1) || num_bytes & (width - 1))) { - return -EINVAL; - } - - return width; -} - -int _mem_safe_read(void *src, char *buf, size_t num_bytes, int width) -{ - width = get_width(src, buf, num_bytes, width); - return unlikely(width < 0) ? -EINVAL : - mem_access(src, buf, width, num_bytes, SYS_MEM_SAFE_READ); -} - -int _mem_safe_write(void *dest, char *buf, size_t num_bytes, int width) -{ - width = get_width(dest, buf, num_bytes, width); - return unlikely(width < 0) ? -EINVAL : - mem_access(dest, buf, width, num_bytes, SYS_MEM_SAFE_WRITE); -} - -#if defined(CONFIG_XIP) -int _mem_safe_write_to_text_section(void *dest, char *buf, size_t num_bytes) -{ - ARG_UNUSED(dest); - ARG_UNUSED(buf); - ARG_UNUSED(num_bytes); - - /* cannot write to text section when it's in ROM */ - return -EFAULT; -} -#else -int _mem_safe_write_to_text_section(void *dest, char *buf, size_t num_bytes) -{ - vaddr_t v = (vaddr_t)dest; - int is_in_text = ((v >= IMAGE_TEXT_START) && - ((v + num_bytes) <= IMAGE_TEXT_END)); - - if (unlikely(!is_in_text)) { - return -EFAULT; - } - - memcpy(dest, buf, num_bytes); - return 0; -} -#endif /* CONFIG_XIP */ - -int _mem_safe_region_add(void *addr, size_t num_bytes, int perm) -{ - if (unlikely(!is_perm_valid(perm))) { - return -EINVAL; - } - - int slot; - int key = irq_lock(); - - if (unlikely(ro_end > rw_end)) { - irq_unlock(key); - return -ENOMEM; - } - - if (perm == SYS_MEM_SAFE_WRITE) { - slot = rw_end; - --rw_end; - } else { - slot = ro_end; - ++ro_end; - } - - mem_regions[slot].addr = (vaddr_t)addr; - mem_regions[slot].last_byte = mem_regions[slot].addr + num_bytes - 1; - - irq_unlock(key); - - return 0; -} - -static int init(struct device *unused) -{ - void *addr; - size_t num_bytes; - - ARG_UNUSED(unused); - - addr = (void *)IMAGE_ROM_START; - num_bytes = (int)(IMAGE_ROM_END - IMAGE_ROM_START); - (void)_mem_safe_region_add(addr, num_bytes, SYS_MEM_SAFE_READ); - - addr = (void *)IMAGE_RAM_START; - num_bytes = (int)(IMAGE_RAM_END - IMAGE_RAM_START); - (void)_mem_safe_region_add(addr, num_bytes, SYS_MEM_SAFE_WRITE); - - return 0; -} - -SYS_INIT(init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/tests/kernel/mem_safe/Makefile b/tests/kernel/mem_safe/Makefile deleted file mode 100644 index 579bb3ce54c..00000000000 --- a/tests/kernel/mem_safe/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -BOARD ?= qemu_x86 -CONF_FILE = prj_$(BOARD).conf - -# This testcase requires a special linker script, from the testcase itself. -# The only boards that have one are the ones listed below. -valid_board_qemu_x86 = y -valid_board_qemu_cortex_m3 = y - -ifneq ($(valid_board_$(BOARD)),y) -$(error not a supported board for this testcase) -endif - -include $(ZEPHYR_BASE)/Makefile.test diff --git a/tests/kernel/mem_safe/README.txt b/tests/kernel/mem_safe/README.txt deleted file mode 100644 index d1c51dc4cb7..00000000000 --- a/tests/kernel/mem_safe/README.txt +++ /dev/null @@ -1,96 +0,0 @@ -Title: Safe Memory Access - -Description: - -This test verifies that the safe memory access (_mem_safe) functions as -intended. - ---------------------------------------------------------------------------- - -Building and Running Project: - -This project outputs to the console. It can be built and executed -on QEMU as follows: - - make run - ---------------------------------------------------------------------------- - -Troubleshooting: - -Problems caused by out-dated project information can be addressed by -issuing one of the following commands then rebuilding the project: - - make clean # discard results of previous builds - # but keep existing configuration info -or - make pristine # discard results of previous builds - # and restore pre-defined configuration info - ---------------------------------------------------------------------------- - -Sample Output: - -testing SUCCESS of READ on RO memory with width 1.......PASS -testing SUCCESS of READ on RO memory with width 2.......PASS -testing SUCCESS of READ on RO memory with width 4.......PASS -testing FAILURE of WRITE on RO memory with width 1.......PASS -testing FAILURE of WRITE on RO memory with width 2.......PASS -testing FAILURE of WRITE on RO memory with width 4.......PASS -testing SUCCESS of READ on RW memory with width 1.......PASS -testing SUCCESS of READ on RW memory with width 2.......PASS -testing SUCCESS of READ on RW memory with width 4.......PASS -testing SUCCESS of WRITE on RW memory with width 1.......PASS -testing SUCCESS of WRITE on RW memory with width 2.......PASS -testing SUCCESS of WRITE on RW memory with width 4.......PASS -testing FAILURE of INVALID ACCESS on RW memory with width 4.......PASS -testing FAILURE of READ on RO memory with width 0.......PASS -testing SUCCESS of READ on RO memory with width 1.......PASS -testing SUCCESS of READ on RO memory with width 2.......PASS -testing FAILURE of READ on RO memory with width 3.......PASS -testing SUCCESS of READ on RO memory with width 4.......PASS -testing FAILURE of READ on RO memory with width 5.......PASS -testing FAILURE of READ on RO memory with width 8.......PASS -testing SUCCESS of READ on RO memory with width 1.......PASS -testing SUCCESS of READ on RO memory with width 1.......PASS -testing SUCCESS of READ on RW memory with width 1.......PASS -testing SUCCESS of READ on RW memory with width 1.......PASS -testing FAILURE of WRITE on RO memory with width 1.......PASS -testing FAILURE of WRITE on RO memory with width 1.......PASS -testing SUCCESS of WRITE on RW memory with width 1.......PASS -testing SUCCESS of WRITE on RW memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing SUCCESS of adding extra RO region.......PASS -testing SUCCESS of adding extra RW region.......PASS -testing FAILURE of adding extra region that won't fit.......PASS -testing SUCCESS of READ on out-of-image memory with width 1.......PASS -testing SUCCESS of READ on out-of-image memory with width 1.......PASS -testing SUCCESS of READ on out-of-image memory with width 1.......PASS -testing SUCCESS of READ on out-of-image memory with width 1.......PASS -testing FAILURE of WRITE on out-of-image memory with width 1.......PASS -testing FAILURE of WRITE on out-of-image memory with width 1.......PASS -testing SUCCESS of WRITE on out-of-image memory with width 1.......PASS -testing SUCCESS of WRITE on out-of-image memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing FAILURE of READ on out-of-image memory with width 1.......PASS -testing FAILURE of WRITE on RO memory with width 1.......PASS -testing SUCCESS of _mem_probe() reading RO values.......PASS -testing SUCCESS of _mem_probe() reading RW values.......PASS -testing SUCCESS of _mem_probe() writing values.......PASS -testing SUCCESS of _mem_safe_read(size: 16, width: 0).......PASS -testing SUCCESS of _mem_safe_read(size: 16, width: 4).......PASS -testing SUCCESS of _mem_safe_read(size: 14, width: 2).......PASS -testing SUCCESS of _mem_safe_read(size: 15, width: 1).......PASS -testing FAILURE of _mem_safe_read() with bad params.......PASS (-22) -testing SUCCESS of _mem_safe_write(size: 16, width: 0).......PASS -testing SUCCESS of _mem_safe_write(size: 16, width: 4).......PASS -testing SUCCESS of _mem_safe_write(size: 14, width: 2).......PASS -testing SUCCESS of _mem_safe_write(size: 15, width: 1).......PASS -testing FAILURE of _mem_safe_write() with bad params.......PASS (-22) -=================================================================== -PASS - main. -=================================================================== -PROJECT EXECUTION SUCCESSFUL diff --git a/tests/kernel/mem_safe/prj_qemu_cortex_m3.conf b/tests/kernel/mem_safe/prj_qemu_cortex_m3.conf deleted file mode 100644 index 42b9285d3fe..00000000000 --- a/tests/kernel/mem_safe/prj_qemu_cortex_m3.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_MEM_SAFE=y -CONFIG_MEM_SAFE_NUM_EXTRA_REGIONS=2 -CONFIG_CACHE_FLUSHING=y diff --git a/tests/kernel/mem_safe/prj_qemu_x86.conf b/tests/kernel/mem_safe/prj_qemu_x86.conf deleted file mode 100644 index 42b9285d3fe..00000000000 --- a/tests/kernel/mem_safe/prj_qemu_x86.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_MEM_SAFE=y -CONFIG_MEM_SAFE_NUM_EXTRA_REGIONS=2 -CONFIG_CACHE_FLUSHING=y diff --git a/tests/kernel/mem_safe/src/Makefile b/tests/kernel/mem_safe/src/Makefile deleted file mode 100644 index 0b337fa8b09..00000000000 --- a/tests/kernel/mem_safe/src/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -ccflags-y += -I${ZEPHYR_BASE}/tests/include -DQUOTE = " -BUILD_ARCH = $(strip $(subst $(DQUOTE),,$(CONFIG_ARCH))) - -obj-y += main.o -obj-y += asm_$(BUILD_ARCH).o diff --git a/tests/kernel/mem_safe/src/asm_arm.S b/tests/kernel/mem_safe/src/asm_arm.S deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/kernel/mem_safe/src/asm_x86.S b/tests/kernel/mem_safe/src/asm_x86.S deleted file mode 100644 index c3dce5cfd14..00000000000 --- a/tests/kernel/mem_safe/src/asm_x86.S +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @ brief architecture specific support for the mem_probe tests - */ - -#include - - GDATA(foo) - GTEXT(add_ten_to_foo) - -SECTION_FUNC(TEXT, add_ten_to_foo) - addl $10, foo - ret diff --git a/tests/kernel/mem_safe/src/main.c b/tests/kernel/mem_safe/src/main.c deleted file mode 100644 index c80dffb7ce5..00000000000 --- a/tests/kernel/mem_safe/src/main.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (c) 2015 Wind River Systems, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @ brief tests for the mem_probe functionalities - */ - -#include -#include -#include -#include -#include -#include - -#define MY_DATA_SIZE 16 -const char __aligned(4) real_rodata[MY_DATA_SIZE] = "0123456789abcdef"; -char *rodata = (char *)real_rodata; -char __aligned(4) rwdata[MY_DATA_SIZE+1]; -char __aligned(4) buffer[MY_DATA_SIZE+1]; - -#if MY_DATA_SIZE != 16 - #error never verified with values other than 16! -#endif - -#define ROM_START ((u32_t)&_image_rom_start) -#define ROM_END ((u32_t)&_image_rom_end) -#define RAM_START ((u32_t)&_image_ram_start) -#define RAM_END ((u32_t)&_image_ram_end) - -char * const p_image_rom_start = (char *)ROM_START; -char * const p_image_rom_end = (char *)ROM_END; -char * const p_image_ram_start = (char *)RAM_START; -char * const p_image_ram_end = (char *)RAM_END; - -char *rw_data_after_image = (char *)(RAM_END + KB(1)); -char *rw_data_after_image_end; -char *ro_data_after_image = (char *)(RAM_END + KB(3)); -char *ro_data_after_image_end; - -int foo; - -#define PROBE_BUFFER_SIZE 32 -char top_of_ram[PROBE_BUFFER_SIZE] __in_section(top_of_image_ram, 0, 0); -char bottom_of_ram[PROBE_BUFFER_SIZE] __in_section(bottom_of_image_ram, 0, 0); - -static void update_rv(int *rv, int last_result) -{ - *rv = *rv == TC_FAIL ? *rv : last_result; - if (last_result == TC_FAIL) { - TC_PRINT("FAIL\n"); - } else { - TC_PRINT("PASS\n"); - } -} - -#define RO 0 -#define RW 1 -#define INVALID -1 -static int mem_range_check(const void *p) -{ - u32_t addr = (u32_t)p; - - if (addr >= ROM_START && addr < ROM_END) { - return RO; - } else if (addr >= RAM_START && addr < RAM_END) { - return RW; - } else { - return INVALID; - } -} - -static int test_width(char *mem, int perm, int width, int expected) -{ - char *rights_str = perm == SYS_MEM_SAFE_READ ? "READ" : - perm == SYS_MEM_SAFE_WRITE ? "WRITE" : - "INVALID ACCESS"; - int mem_type = mem_range_check(mem); - char *mem_range_str = mem_type == RO ? "RO" : - mem_type == RW ? "RW" : "out-of-image"; - - TC_PRINT("testing %s of %s on %s memory with width %d.......", - expected == 0 ? "SUCCESS" : "FAILURE", - rights_str, mem_range_str, width); - - int rv = _mem_probe(mem, perm, width, buffer); - - return rv == expected ? TC_PASS : TC_FAIL; -} - -static int test_width_read(char *mem, int width, int expected) -{ - return test_width(mem, SYS_MEM_SAFE_READ, width, expected); -} - -static int test_width_write(char *mem, int width, int expected) -{ - return test_width(mem, SYS_MEM_SAFE_WRITE, width, expected); -} - -typedef int (*access_func)(void *, char *, size_t, int); -static int test_mem_safe_access(void *p, char *buf, int size, - int width, int perm) -{ - int rc; - char *func_str = (perm == SYS_MEM_SAFE_WRITE) ? "write" : "read"; - - access_func func = (perm == SYS_MEM_SAFE_WRITE) ? - _mem_safe_write : _mem_safe_read; - - TC_PRINT("testing SUCCESS of _mem_safe_%s(size: %d, width: %d).......", - func_str, size, width); - rc = func(p, buf, size, width); - if (rc < 0) { - TC_PRINT("(%d)", rc); - return TC_FAIL; - } - - if (memcmp(p, buf, size) != 0) { - TC_PRINT("(bad data)"); - return TC_FAIL; - } - - return TC_PASS; -} - -void main(void) -{ - /* reference symbols so that they get included */ - top_of_ram[0] = 'a'; - bottom_of_ram[PROBE_BUFFER_SIZE - 1] = 'z'; - - int rv = TC_PASS; - int rc; /* temporary return code */ - - buffer[MY_DATA_SIZE] = '\0'; - rwdata[MY_DATA_SIZE] = '\0'; - - TC_START("safe memory access routines\n"); - - /**** - * _mem_probe() - */ - - /* test access perm */ - - update_rv(&rv, test_width_read(rodata, 1, 0)); - update_rv(&rv, test_width_read(rodata, 2, 0)); - update_rv(&rv, test_width_read(rodata, 4, 0)); - - update_rv(&rv, test_width_write(rodata, 1, -EFAULT)); - update_rv(&rv, test_width_write(rodata, 2, -EFAULT)); - update_rv(&rv, test_width_write(rodata, 4, -EFAULT)); - - update_rv(&rv, test_width_read(rwdata, 1, 0)); - update_rv(&rv, test_width_read(rwdata, 2, 0)); - update_rv(&rv, test_width_read(rwdata, 4, 0)); - - update_rv(&rv, test_width_write(rwdata, 1, 0)); - update_rv(&rv, test_width_write(rwdata, 2, 0)); - update_rv(&rv, test_width_write(rwdata, 4, 0)); - - const int invalid_access_right = 3; - - update_rv(&rv, test_width(rwdata, invalid_access_right, 4, -EINVAL)); - - /* test alignments constraints */ - - update_rv(&rv, test_width_read(rodata, 0, -EINVAL)); - update_rv(&rv, test_width_read(rodata, 1, 0)); - update_rv(&rv, test_width_read(rodata, 2, 0)); - update_rv(&rv, test_width_read(rodata, 3, -EINVAL)); - update_rv(&rv, test_width_read(rodata, 4, 0)); - update_rv(&rv, test_width_read(rodata, 5, -EINVAL)); - update_rv(&rv, test_width_read(rodata, 8, -EINVAL)); - - /* test image limits */ - - update_rv(&rv, test_width_read(p_image_rom_start, 1, 0)); - update_rv(&rv, test_width_read(p_image_rom_end - 1, 1, 0)); - update_rv(&rv, test_width_read(p_image_ram_start, 1, 0)); - update_rv(&rv, test_width_read(p_image_ram_end - 1, 1, 0)); - - update_rv(&rv, test_width_write(p_image_rom_start, 1, -EFAULT)); - update_rv(&rv, test_width_write(p_image_rom_end - 1, 1, -EFAULT)); - update_rv(&rv, test_width_write(p_image_ram_start, 1, 0)); - update_rv(&rv, test_width_write(p_image_ram_end - 1, 1, 0)); - - update_rv(&rv, test_width_read(p_image_rom_start - 1, 1, -EFAULT)); - update_rv(&rv, test_width_read(p_image_ram_end, 1, -EFAULT)); - - /* test out-of-image valid regions */ - - rw_data_after_image_end = rw_data_after_image + KB(1); - ro_data_after_image_end = ro_data_after_image + KB(1); - - TC_PRINT("testing SUCCESS of adding extra RO region......."); - int region_add_rc = _mem_safe_region_add(ro_data_after_image, KB(1), - SYS_MEM_SAFE_READ); - if (region_add_rc < 0) { - update_rv(&rv, TC_FAIL); - TC_PRINT("FAIL (%d)\n", region_add_rc); - } else { - TC_PRINT("PASS\n"); - } - - TC_PRINT("testing SUCCESS of adding extra RW region......."); - region_add_rc = _mem_safe_region_add(rw_data_after_image, KB(1), - SYS_MEM_SAFE_WRITE); - if (region_add_rc < 0) { - update_rv(&rv, TC_FAIL); - TC_PRINT("FAIL (%d)\n", region_add_rc); - } else { - TC_PRINT("PASS\n"); - } - - TC_PRINT("testing FAILURE of adding extra region that won't fit......."); - region_add_rc = _mem_safe_region_add(rw_data_after_image, KB(1), - SYS_MEM_SAFE_WRITE); - if (region_add_rc < 0) { - TC_PRINT("PASS\n"); - } else { - TC_PRINT("FAIL\n"); - } - - update_rv(&rv, test_width_read(ro_data_after_image, 1, 0)); - update_rv(&rv, test_width_read(ro_data_after_image_end - 1, 1, 0)); - update_rv(&rv, test_width_read(rw_data_after_image, 1, 0)); - update_rv(&rv, test_width_read(rw_data_after_image_end - 1, 1, 0)); - - update_rv(&rv, test_width_write(ro_data_after_image, 1, -EFAULT)); - update_rv(&rv, test_width_write(ro_data_after_image_end - 1, 1, -EFAULT)); - update_rv(&rv, test_width_write(rw_data_after_image, 1, 0)); - update_rv(&rv, test_width_write(rw_data_after_image_end - 1, 1, 0)); - - update_rv(&rv, test_width_read(ro_data_after_image - 1, 1, -EFAULT)); - update_rv(&rv, test_width_read(ro_data_after_image_end, 1, -EFAULT)); - update_rv(&rv, test_width_read(rw_data_after_image - 1, 1, -EFAULT)); - update_rv(&rv, test_width_read(rw_data_after_image_end, 1, -EFAULT)); - - /* - * Test the dividing line between rom and ram, even in non-xip images: - * it might hit ROM or invalid memory, but never RAM. - */ - update_rv(&rv, test_width_write(p_image_ram_start - 1, 1, -EFAULT)); - - TC_PRINT("testing SUCCESS of _mem_probe() reading RO values......."); - (void)_mem_probe(rodata, SYS_MEM_SAFE_READ, 4, buffer); - (void)_mem_probe(rodata+4, SYS_MEM_SAFE_READ, 4, buffer+4); - (void)_mem_probe(rodata+8, SYS_MEM_SAFE_READ, 2, buffer+8); - (void)_mem_probe(rodata+10, SYS_MEM_SAFE_READ, 2, buffer+10); - (void)_mem_probe(rodata+12, SYS_MEM_SAFE_READ, 1, buffer+12); - (void)_mem_probe(rodata+13, SYS_MEM_SAFE_READ, 1, buffer+13); - (void)_mem_probe(rodata+14, SYS_MEM_SAFE_READ, 1, buffer+14); - (void)_mem_probe(rodata+15, SYS_MEM_SAFE_READ, 1, buffer+15); - - if (memcmp(rodata, buffer, 16) != 0) { - TC_PRINT("FAIL\n"); - update_rv(&rv, TC_FAIL); - } else { - TC_PRINT("PASS\n"); - } - - memcpy(rwdata, rodata, MY_DATA_SIZE); - memset(buffer, '-', MY_DATA_SIZE); - - TC_PRINT("testing SUCCESS of _mem_probe() reading RW values......."); - (void)_mem_probe(rwdata, SYS_MEM_SAFE_READ, 4, buffer); - (void)_mem_probe(rwdata+4, SYS_MEM_SAFE_READ, 4, buffer+4); - (void)_mem_probe(rwdata+8, SYS_MEM_SAFE_READ, 2, buffer+8); - (void)_mem_probe(rwdata+10, SYS_MEM_SAFE_READ, 2, buffer+10); - (void)_mem_probe(rwdata+12, SYS_MEM_SAFE_READ, 1, buffer+12); - (void)_mem_probe(rwdata+13, SYS_MEM_SAFE_READ, 1, buffer+13); - (void)_mem_probe(rwdata+14, SYS_MEM_SAFE_READ, 1, buffer+14); - (void)_mem_probe(rwdata+15, SYS_MEM_SAFE_READ, 1, buffer+15); - - if (memcmp(rwdata, buffer, 16) != 0) { - TC_PRINT("FAIL\n"); - update_rv(&rv, TC_FAIL); - } else { - TC_PRINT("PASS\n"); - } - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - - TC_PRINT("testing SUCCESS of _mem_probe() writing values......."); - (void)_mem_probe(rwdata, SYS_MEM_SAFE_WRITE, 4, buffer); - (void)_mem_probe(rwdata+4, SYS_MEM_SAFE_WRITE, 4, buffer+4); - (void)_mem_probe(rwdata+8, SYS_MEM_SAFE_WRITE, 2, buffer+8); - (void)_mem_probe(rwdata+10, SYS_MEM_SAFE_WRITE, 2, buffer+10); - (void)_mem_probe(rwdata+12, SYS_MEM_SAFE_WRITE, 1, buffer+12); - (void)_mem_probe(rwdata+13, SYS_MEM_SAFE_WRITE, 1, buffer+13); - (void)_mem_probe(rwdata+14, SYS_MEM_SAFE_WRITE, 1, buffer+14); - (void)_mem_probe(rwdata+15, SYS_MEM_SAFE_WRITE, 1, buffer+15); - - if (memcmp(rwdata, buffer, 16) != 0) { - TC_PRINT("FAIL\n"); - update_rv(&rv, TC_FAIL); - } else { - TC_PRINT("PASS\n"); - } - - /***** - * _mem_safe_read() - */ - - memset(buffer, '-', MY_DATA_SIZE); - - update_rv(&rv, test_mem_safe_access(rodata, buffer, MY_DATA_SIZE, - 0, SYS_MEM_SAFE_READ)); - - update_rv(&rv, test_mem_safe_access(rodata, buffer, MY_DATA_SIZE, - 4, SYS_MEM_SAFE_READ)); - - update_rv(&rv, test_mem_safe_access(rodata, buffer, MY_DATA_SIZE-2, - 2, SYS_MEM_SAFE_READ)); - - update_rv(&rv, test_mem_safe_access(rodata, buffer, MY_DATA_SIZE-1, - 1, SYS_MEM_SAFE_READ)); - - TC_PRINT("testing FAILURE of _mem_safe_read() with bad params......."); - rc = _mem_safe_read(rodata+1, buffer, MY_DATA_SIZE-1, 2); - if (rc == 0) { - TC_PRINT("FAIL\n"); - update_rv(&rv, TC_FAIL); - } else { - TC_PRINT("PASS (%d)\n", rc); - } - - - /***** - * _mem_safe_write() - */ - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - update_rv(&rv, test_mem_safe_access(rwdata, buffer, MY_DATA_SIZE, - 0, SYS_MEM_SAFE_WRITE)); - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - update_rv(&rv, test_mem_safe_access(rwdata, buffer, MY_DATA_SIZE, - 4, SYS_MEM_SAFE_WRITE)); - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - update_rv(&rv, test_mem_safe_access(rwdata, buffer, MY_DATA_SIZE - 2, - 2, SYS_MEM_SAFE_WRITE)); - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - update_rv(&rv, test_mem_safe_access(rwdata, buffer, MY_DATA_SIZE - 1, - 1, SYS_MEM_SAFE_WRITE)); - - memcpy(buffer, rodata, MY_DATA_SIZE); - memset(rwdata, '-', MY_DATA_SIZE); - - TC_PRINT("testing FAILURE of _mem_safe_write() with bad params......."); - rc = _mem_safe_write(rwdata+1, buffer, MY_DATA_SIZE-1, 2); - if (rc == 0) { - TC_PRINT("FAIL\n"); - update_rv(&rv, TC_FAIL); - } else { - TC_PRINT("PASS (%d)\n", rc); - } - -#if !defined(CONFIG_XIP) - /***** - * _mem_safe_write_to_text_section() - */ - - extern void add_ten_to_foo(void); - foo = 0; - memset(buffer, 0x90, 7); - - TC_PRINT("testing FAILURE of _mem_safe_write_to_text_section(&data)......."); - if (_mem_safe_write_to_text_section(&foo, buffer, 1) == 0) { - TC_PRINT("FAIL\n"); - } else { - TC_PRINT("PASS\n"); - } - - TC_PRINT("testing SUCCESS of _mem_safe_write_to_text_section(&text)......."); - add_ten_to_foo(); - if (foo != 10) { - TC_PRINT("FAIL\n"); - } else { - if (_mem_safe_write_to_text_section((void *)add_ten_to_foo, - buffer, 7) < 0) { - TC_PRINT("FAIL\n"); - } else { - - sys_cache_flush((vaddr_t)add_ten_to_foo, 7); - add_ten_to_foo(); - if (foo != 10) { - TC_PRINT("FAIL\n"); - } else { - TC_PRINT("PASS\n"); - } - } - } -#endif - - TC_END_RESULT(rv); - TC_END_REPORT(rv); -} diff --git a/tests/kernel/mem_safe/testcase.ini b/tests/kernel/mem_safe/testcase.ini deleted file mode 100644 index 97ac746aeec..00000000000 --- a/tests/kernel/mem_safe/testcase.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test] -tags = kernel core -platform_whitelist = qemu_x86 qemu_cortex_m3 -filter = not CONFIG_X86_IAMCU -timeout = 200