rtos: Add support for Zephyr RTOS
authorEvgeniy Didin <didin@synopsys.com>
Tue, 16 Feb 2021 18:53:41 +0000 (21:53 +0300)
committerOleksij Rempel <linux@rempel-privat.de>
Tue, 11 May 2021 09:43:22 +0000 (10:43 +0100)
With this patch, the Zephyr[1] RTOS is supported by OpenOCD.

As usual with support for other RTOSes, Zephyr must be compiled with
the DEBUG_THREAD_INFO option. This will generate some symbols
with information needed in order to build the list of threads.

The current implementation is limited to Zephyr running on ARM
Cortex-M processors. This is the only ARM variant supported by Zephyr
at the moment and is used on most of the officially supported boards.

[1] https://www.zephyrproject.org/

Change-Id: I22afdbec91562f3a22cf5b88cd4ea3a7a59ba0b4
Signed-off-by: Evgeniy Didin <didin@synopsys.com>
Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
Signed-off-by: Daniel Glöckner <dg@emlix.com>
Reviewed-on: http://openocd.zylin.com/4988
Tested-by: jenkins
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
doc/openocd.texi
src/rtos/Makefile.am
src/rtos/rtos.c
src/rtos/zephyr.c [new file with mode: 0644]

index 6614f4832fb5e57f91c4ab123fb5f27591170ede..fc448339f00f17121f9d4514900d1edbc07df0be 100644 (file)
@@ -4681,7 +4681,7 @@ The value should normally correspond to a static mapping for the
 @var{rtos_type} can be one of @option{auto}, @option{eCos},
 @option{ThreadX}, @option{FreeRTOS}, @option{linux}, @option{ChibiOS},
 @option{embKernel}, @option{mqx}, @option{uCOS-III}, @option{nuttx},
-@option{RIOT}
+@option{RIOT}, @option{Zephyr}
 @xref{gdbrtossupport,,RTOS Support}.
 
 @item @code{-defer-examine} -- skip target examination at initial JTAG chain
@@ -10950,6 +10950,7 @@ Currently supported rtos's include:
 @item @option{nuttx}
 @item @option{RIOT}
 @item @option{hwthread} (This is not an actual RTOS. @xref{usingopenocdsmpwithgdb,,Using OpenOCD SMP with GDB}.)
+@item @option{Zephyr}
 @end itemize
 
 Before an RTOS can be detected, it must export certain symbols; otherwise, it cannot
@@ -10984,12 +10985,17 @@ g_readytorun, g_tasklisttable.
 sched_threads, sched_num_threads, sched_active_pid, max_threads,
 _tcb_name_offset.
 @end raggedright
+@item Zephyr symbols
+_kernel, _kernel_openocd_offsets, _kernel_openocd_size_t_size
 @end table
 
 For most RTOS supported the above symbols will be exported by default. However for
-some, eg. FreeRTOS and uC/OS-III, extra steps must be taken.
+some, eg. FreeRTOS, uC/OS-III and Zephyr, extra steps must be taken.
 
-These RTOSes may require additional OpenOCD-specific file to be linked
+Zephyr must be compiled with the DEBUG_THREAD_INFO option. This will generate some symbols
+with information needed in order to build the list of threads.
+
+FreeRTOS and uC/OS-III RTOSes may require additional OpenOCD-specific file to be linked
 along with the project:
 
 @table @code
index de54596cd424bec7f89879fcd81cadf7abce9750..49cb830e5aedbe09fa58a2f48c4f369b7106dec7 100644 (file)
@@ -19,6 +19,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
        %D%/uCOS-III.c \
        %D%/nuttx.c \
        %D%/hwthread.c \
+       %D%/zephyr.c \
        %D%/riot.c \
        %D%/rtos.h \
        %D%/rtos_standard_stackings.h \
index 91d9379f30395619c7797a3a178b9341f990532d..fdb3862d8ba74c241ce87ee38be5226466e0650d 100644 (file)
@@ -39,6 +39,7 @@ extern struct rtos_type uCOS_III_rtos;
 extern struct rtos_type nuttx_rtos;
 extern struct rtos_type hwthread_rtos;
 extern struct rtos_type riot_rtos;
+extern struct rtos_type zephyr_rtos;
 
 static struct rtos_type *rtos_types[] = {
        &ThreadX_rtos,
@@ -52,6 +53,7 @@ static struct rtos_type *rtos_types[] = {
        &uCOS_III_rtos,
        &nuttx_rtos,
        &riot_rtos,
+       &zephyr_rtos,
        /* keep this as last, as it always matches with rtos auto */
        &hwthread_rtos,
        NULL
diff --git a/src/rtos/zephyr.c b/src/rtos/zephyr.c
new file mode 100644 (file)
index 0000000..e5d6835
--- /dev/null
@@ -0,0 +1,788 @@
+/***************************************************************************
+ *   Copyright (C) 2017 by Intel Corporation
+ *   Leandro Pereira <leandro.pereira@intel.com>
+ *   Daniel Glöckner <dg@emlix.com>*
+ *   Copyright (C) 2021 by Synopsys, Inc.
+ *   Evgeniy Didin <didin@synopsys.com>
+ *                                                                         *
+ *   SPDX-License-Identifier: GPL-2.0-or-later                             *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/time_support.h>
+#include <jtag/jtag.h>
+
+#include "helper/log.h"
+#include "helper/types.h"
+#include "rtos.h"
+#include "rtos_standard_stackings.h"
+#include "target/target.h"
+#include "target/target_type.h"
+#include "target/armv7m.h"
+#include "target/arc.h"
+
+#define UNIMPLEMENTED 0xFFFFFFFFU
+
+/* ARC specific defines */
+#define ARC_AUX_SEC_BUILD_REG 0xdb
+#define ARC_REG_NUM 38
+
+/* ARM specific defines */
+#define ARM_XPSR_OFFSET 28
+
+struct zephyr_thread {
+       uint32_t ptr, next_ptr;
+       uint32_t entry;
+       uint32_t stack_pointer;
+       uint8_t state;
+       uint8_t user_options;
+       int8_t prio;
+       char name[64];
+};
+
+enum zephyr_offsets {
+       OFFSET_VERSION,
+       OFFSET_K_CURR_THREAD,
+       OFFSET_K_THREADS,
+       OFFSET_T_ENTRY,
+       OFFSET_T_NEXT_THREAD,
+       OFFSET_T_STATE,
+       OFFSET_T_USER_OPTIONS,
+       OFFSET_T_PRIO,
+       OFFSET_T_STACK_POINTER,
+       OFFSET_T_NAME,
+       OFFSET_T_ARCH,
+       OFFSET_T_PREEMPT_FLOAT,
+       OFFSET_T_COOP_FLOAT,
+       OFFSET_MAX
+};
+
+struct zephyr_params {
+       const char *target_name;
+       uint8_t size_width;
+       uint8_t pointer_width;
+       uint32_t num_offsets;
+       uint32_t offsets[OFFSET_MAX];
+       const struct rtos_register_stacking *callee_saved_stacking;
+       const struct rtos_register_stacking *cpu_saved_nofp_stacking;
+       const struct rtos_register_stacking *cpu_saved_fp_stacking;
+       int (*get_cpu_state)(struct rtos *rtos, target_addr_t *addr,
+                       struct zephyr_params *params,
+                       struct rtos_reg *callee_saved_reg_list,
+                       struct rtos_reg **reg_list, int *num_regs);
+};
+
+static const struct stack_register_offset arm_callee_saved[] = {
+       { ARMV7M_R13, 32, 32 },
+       { ARMV7M_R4,  0,  32 },
+       { ARMV7M_R5,  4,  32 },
+       { ARMV7M_R6,  8,  32 },
+       { ARMV7M_R7,  12, 32 },
+       { ARMV7M_R8,  16, 32 },
+       { ARMV7M_R9,  20, 32 },
+       { ARMV7M_R10, 24, 32 },
+       { ARMV7M_R11, 28, 32 },
+};
+
+static const struct stack_register_offset arc_callee_saved[] = {
+       { ARC_R13,  0,  32 },
+       { ARC_R14,  4,  32 },
+       { ARC_R15,  8,  32 },
+       { ARC_R16,  12,  32 },
+       { ARC_R17,  16,  32 },
+       { ARC_R18,  20,  32 },
+       { ARC_R19,  24,  32 },
+       { ARC_R20,  28,  32 },
+       { ARC_R21,  32,  32 },
+       { ARC_R22,  36,  32 },
+       { ARC_R23,  40,  32 },
+       { ARC_R24,  44,  32 },
+       { ARC_R25,  48,  32 },
+       { ARC_GP,  52,  32 },
+       { ARC_FP,  56,  32 },
+       { ARC_R30,  60,  32 }
+};
+static const struct rtos_register_stacking arm_callee_saved_stacking = {
+       .stack_registers_size = 36,
+       .stack_growth_direction = -1,
+       .num_output_registers = ARRAY_SIZE(arm_callee_saved),
+       .register_offsets = arm_callee_saved,
+};
+
+static const struct rtos_register_stacking arc_callee_saved_stacking = {
+       .stack_registers_size = 64,
+       .stack_growth_direction = -1,
+       .num_output_registers = ARRAY_SIZE(arc_callee_saved),
+       .register_offsets = arc_callee_saved,
+};
+
+static const struct stack_register_offset arm_cpu_saved[] = {
+       { ARMV7M_R0,   0,  32 },
+       { ARMV7M_R1,   4,  32 },
+       { ARMV7M_R2,   8,  32 },
+       { ARMV7M_R3,   12, 32 },
+       { ARMV7M_R4,   -1, 32 },
+       { ARMV7M_R5,   -1, 32 },
+       { ARMV7M_R6,   -1, 32 },
+       { ARMV7M_R7,   -1, 32 },
+       { ARMV7M_R8,   -1, 32 },
+       { ARMV7M_R9,   -1, 32 },
+       { ARMV7M_R10,  -1, 32 },
+       { ARMV7M_R11,  -1, 32 },
+       { ARMV7M_R12,  16, 32 },
+       { ARMV7M_R13,  -2, 32 },
+       { ARMV7M_R14,  20, 32 },
+       { ARMV7M_PC,   24, 32 },
+       { ARMV7M_xPSR, 28, 32 },
+};
+
+static struct stack_register_offset arc_cpu_saved[] = {
+       { ARC_R0,               -1,  32 },
+       { ARC_R1,               -1,  32 },
+       { ARC_R2,               -1,  32 },
+       { ARC_R3,               -1,  32 },
+       { ARC_R4,               -1,  32 },
+       { ARC_R5,               -1,  32 },
+       { ARC_R6,               -1,  32 },
+       { ARC_R7,               -1,  32 },
+       { ARC_R8,               -1,  32 },
+       { ARC_R9,               -1,  32 },
+       { ARC_R10,              -1,  32 },
+       { ARC_R11,              -1,  32 },
+       { ARC_R12,              -1,  32 },
+       { ARC_R13,              -1,  32 },
+       { ARC_R14,              -1,  32 },
+       { ARC_R15,              -1,  32 },
+       { ARC_R16,              -1,  32 },
+       { ARC_R17,              -1,  32 },
+       { ARC_R18,              -1,  32 },
+       { ARC_R19,              -1,  32 },
+       { ARC_R20,              -1,  32 },
+       { ARC_R21,              -1,  32 },
+       { ARC_R22,              -1,  32 },
+       { ARC_R23,              -1,  32 },
+       { ARC_R24,              -1,  32 },
+       { ARC_R25,              -1,  32 },
+       { ARC_GP,               -1,  32 },
+       { ARC_FP,               -1,  32 },
+       { ARC_SP,               -1,  32 },
+       { ARC_ILINK,            -1,  32 },
+       { ARC_R30,              -1,  32 },
+       { ARC_BLINK,             0,  32 },
+       { ARC_LP_COUNT,         -1,  32 },
+       { ARC_PCL,              -1,  32 },
+       { ARC_PC,               -1,  32 },
+       { ARC_LP_START,         -1,  32 },
+       { ARC_LP_END,           -1,  32 },
+       { ARC_STATUS32,          4,  32 }
+};
+
+
+enum zephyr_symbol_values {
+       ZEPHYR_VAL__KERNEL,
+       ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS,
+       ZEPHYR_VAL__KERNEL_OPENOCD_SIZE_T_SIZE,
+       ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS,
+       ZEPHYR_VAL_COUNT
+};
+
+static int64_t zephyr_cortex_m_stack_align(struct target *target,
+               const uint8_t *stack_data,
+               const struct rtos_register_stacking *stacking, int64_t stack_ptr)
+{
+       return rtos_Cortex_M_stack_align(target, stack_data, stacking,
+                       stack_ptr, ARM_XPSR_OFFSET);
+}
+
+static const struct rtos_register_stacking arm_cpu_saved_nofp_stacking = {
+       .stack_registers_size = 32,
+       .stack_growth_direction = -1,
+       .num_output_registers = ARRAY_SIZE(arm_cpu_saved),
+       .calculate_process_stack = zephyr_cortex_m_stack_align,
+       .register_offsets = arm_cpu_saved,
+};
+
+static const struct rtos_register_stacking arm_cpu_saved_fp_stacking = {
+       .stack_registers_size = 32 + 18 * 4,
+       .stack_growth_direction = -1,
+       .num_output_registers = ARRAY_SIZE(arm_cpu_saved),
+       .calculate_process_stack = zephyr_cortex_m_stack_align,
+       .register_offsets = arm_cpu_saved,
+};
+
+/* stack_registers_size is 8 because besides caller registers
+ * there are only blink and Status32 registers on stack left */
+static struct rtos_register_stacking arc_cpu_saved_stacking = {
+       .stack_registers_size = 8,
+       .stack_growth_direction = -1,
+       .num_output_registers = ARRAY_SIZE(arc_cpu_saved),
+       .register_offsets = arc_cpu_saved,
+};
+
+/* ARCv2 specific implementation */
+static int zephyr_get_arc_state(struct rtos *rtos, target_addr_t *addr,
+                        struct zephyr_params *params,
+                        struct rtos_reg *callee_saved_reg_list,
+                        struct rtos_reg **reg_list, int *num_regs)
+{
+
+       uint32_t real_stack_addr;
+       int retval = 0;
+       int num_callee_saved_regs;
+       const struct rtos_register_stacking *stacking;
+
+       /* Getting real stack address from Kernel thread struct */
+       retval = target_read_u32(rtos->target, *addr, &real_stack_addr);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Getting callee registers */
+       retval = rtos_generic_stack_read(rtos->target,
+                       params->callee_saved_stacking,
+                       real_stack_addr, &callee_saved_reg_list,
+                       &num_callee_saved_regs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       stacking = params->cpu_saved_nofp_stacking;
+
+       /* Getting blink and status32 registers */
+       retval = rtos_generic_stack_read(rtos->target, stacking,
+                       real_stack_addr + num_callee_saved_regs * 4,
+                       reg_list, num_regs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (int i = 0; i < num_callee_saved_regs; i++)
+               buf_cpy(callee_saved_reg_list[i].value,
+                       (*reg_list)[callee_saved_reg_list[i].number].value,
+                       callee_saved_reg_list[i].size);
+
+       /* The blink, sp, pc offsets in arc_cpu_saved structure may be changed,
+        * but the registers number shall not. So the next code searches the
+        * offsetst of these registers in arc_cpu_saved structure. */
+       unsigned short blink_offset = 0, pc_offset = 0, sp_offset = 0;
+       for (size_t i = 0; i < ARRAY_SIZE(arc_cpu_saved); i++) {
+               if (arc_cpu_saved[i].number == ARC_BLINK)
+                       blink_offset = i;
+               if (arc_cpu_saved[i].number == ARC_SP)
+                       sp_offset = i;
+               if (arc_cpu_saved[i].number == ARC_PC)
+                       pc_offset = i;
+       }
+
+       if (blink_offset == 0 || sp_offset == 0 || pc_offset == 0) {
+               LOG_ERROR("Basic registers offsets are missing, check <arc_cpu_saved> struct");
+               return ERROR_FAIL;
+       }
+
+       /* Put blink value into PC */
+       buf_cpy((*reg_list)[blink_offset].value,
+               (*reg_list)[pc_offset].value, sizeof((*reg_list)[blink_offset].value));
+
+       /* Put address after callee/caller in SP. */
+       int64_t stack_top;
+
+       stack_top = real_stack_addr + num_callee_saved_regs * 4
+                       + arc_cpu_saved_stacking.stack_registers_size;
+       buf_cpy(&stack_top, (*reg_list)[sp_offset].value, sizeof(stack_top));
+
+       return retval;
+}
+
+/* ARM Cortex-M-specific implementation */
+static int zephyr_get_arm_state(struct rtos *rtos, target_addr_t *addr,
+                        struct zephyr_params *params,
+                        struct rtos_reg *callee_saved_reg_list,
+                        struct rtos_reg **reg_list, int *num_regs)
+{
+
+       int retval = 0;
+       int num_callee_saved_regs;
+       const struct rtos_register_stacking *stacking;
+
+       retval = rtos_generic_stack_read(rtos->target,
+                       params->callee_saved_stacking,
+                       *addr, &callee_saved_reg_list,
+                       &num_callee_saved_regs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *addr = target_buffer_get_u32(rtos->target,
+                       callee_saved_reg_list[0].value);
+
+       if (params->offsets[OFFSET_T_PREEMPT_FLOAT] != UNIMPLEMENTED)
+               stacking = params->cpu_saved_fp_stacking;
+       else
+               stacking = params->cpu_saved_nofp_stacking;
+
+       retval = rtos_generic_stack_read(rtos->target, stacking, *addr, reg_list,
+                       num_regs);
+       if (retval != ERROR_OK)
+               return retval;
+
+       for (int i = 1; i < num_callee_saved_regs; i++)
+               buf_cpy(callee_saved_reg_list[i].value,
+                       (*reg_list)[callee_saved_reg_list[i].number].value,
+                       callee_saved_reg_list[i].size);
+       return 0;
+}
+
+static struct zephyr_params zephyr_params_list[] = {
+       {
+               .target_name = "cortex_m",
+               .pointer_width = 4,
+               .callee_saved_stacking = &arm_callee_saved_stacking,
+               .cpu_saved_nofp_stacking = &arm_cpu_saved_nofp_stacking,
+               .cpu_saved_fp_stacking = &arm_cpu_saved_fp_stacking,
+               .get_cpu_state = &zephyr_get_arm_state,
+       },
+       {
+               .target_name = "hla_target",
+               .pointer_width = 4,
+               .callee_saved_stacking = &arm_callee_saved_stacking,
+               .cpu_saved_nofp_stacking = &arm_cpu_saved_nofp_stacking,
+               .cpu_saved_fp_stacking = &arm_cpu_saved_fp_stacking,
+               .get_cpu_state = &zephyr_get_arm_state,
+
+       },
+       {
+               .target_name = "arcv2",
+               .pointer_width = 4,
+               .callee_saved_stacking = &arc_callee_saved_stacking,
+               .cpu_saved_nofp_stacking = &arc_cpu_saved_stacking,
+               .get_cpu_state = &zephyr_get_arc_state,
+       },
+       {
+               .target_name = NULL
+       }
+};
+
+static const struct symbol_table_elem zephyr_symbol_list[] = {
+       {
+               .symbol_name = "_kernel",
+               .optional = false
+       },
+       {
+               .symbol_name = "_kernel_openocd_offsets",
+               .optional = false
+       },
+       {
+               .symbol_name = "_kernel_openocd_size_t_size",
+               .optional = false
+       },
+       {
+               .symbol_name = "_kernel_openocd_num_offsets",
+               .optional = true
+       },
+       {
+               .symbol_name = NULL
+       }
+};
+
+static bool zephyr_detect_rtos(struct target *target)
+{
+       if (target->rtos->symbols == NULL) {
+               LOG_INFO("Zephyr: no symbols while detecting RTOS");
+               return false;
+       }
+
+       for (enum zephyr_symbol_values symbol = ZEPHYR_VAL__KERNEL;
+                                       symbol != ZEPHYR_VAL_COUNT; symbol++) {
+               LOG_INFO("Zephyr: does it have symbol %d (%s)?", symbol,
+                       target->rtos->symbols[symbol].optional ? "optional" : "mandatory");
+
+               if (target->rtos->symbols[symbol].optional)
+                       continue;
+               if (target->rtos->symbols[symbol].address == 0)
+                       return false;
+       }
+
+       LOG_INFO("Zephyr: all mandatory symbols found");
+
+       return true;
+}
+
+static int zephyr_create(struct target *target)
+{
+       const char *name;
+
+       name = target_type_name(target);
+
+       LOG_INFO("Zephyr: looking for target: %s", name);
+
+       /* ARC specific, check if EM target has security subsystem
+        * In case of ARC_HAS_SECURE zephyr option enabled
+        * the thread stack contains blink,sec_stat,status32 register
+        * values. If ARC_HAS_SECURE is disabled, only blink and status32
+        * register values are saved on stack. */
+       if (!strcmp(name, "arcv2")) {
+               uint32_t value;
+               struct arc_common *arc = target_to_arc(target);
+               /* Reading SEC_BUILD bcr */
+               CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc->jtag_info, ARC_AUX_SEC_BUILD_REG, &value));
+               if (value != 0) {
+                       LOG_DEBUG("ARC EM board has security subsystem, changing offsets");
+                       arc_cpu_saved[ARC_REG_NUM - 1].offset = 8;
+                       /* After reading callee registers in stack
+                        * now blink,sec_stat,status32 registers
+                        * are located. */
+                       arc_cpu_saved_stacking.stack_registers_size = 12;
+               }
+       }
+
+       for (struct zephyr_params *p = zephyr_params_list; p->target_name; p++) {
+               if (!strcmp(p->target_name, name)) {
+                       LOG_INFO("Zephyr: target known, params at %p", p);
+                       target->rtos->rtos_specific_params = p;
+                       return ERROR_OK;
+               }
+       }
+
+       LOG_ERROR("Could not find target in Zephyr compatibility list");
+       return ERROR_FAIL;
+}
+
+struct zephyr_array {
+       void *ptr;
+       size_t elements;
+};
+
+static void zephyr_array_init(struct zephyr_array *array)
+{
+       array->ptr = NULL;
+       array->elements = 0;
+}
+
+static void zephyr_array_free(struct zephyr_array *array)
+{
+       free(array->ptr);
+       zephyr_array_init(array);
+}
+
+static void *zephyr_array_append(struct zephyr_array *array, size_t size)
+{
+       if (!(array->elements % 16)) {
+               void *ptr = realloc(array->ptr, (array->elements + 16) * size);
+
+               if (!ptr) {
+                       LOG_ERROR("Out of memory");
+                       return NULL;
+               }
+
+               array->ptr = ptr;
+       }
+
+       return (unsigned char *)array->ptr + (array->elements++) * size;
+}
+
+static void *zephyr_array_detach_ptr(struct zephyr_array *array)
+{
+       void *ptr = array->ptr;
+
+       zephyr_array_init(array);
+
+       return ptr;
+}
+
+static uint32_t zephyr_kptr(const struct rtos *rtos, enum zephyr_offsets off)
+{
+       const struct zephyr_params *params = rtos->rtos_specific_params;
+
+       return rtos->symbols[ZEPHYR_VAL__KERNEL].address + params->offsets[off];
+}
+
+static int zephyr_fetch_thread(const struct rtos *rtos,
+                               struct zephyr_thread *thread, uint32_t ptr)
+{
+       const struct zephyr_params *param = rtos->rtos_specific_params;
+       int retval;
+
+       thread->ptr = ptr;
+
+       retval = target_read_u32(rtos->target, ptr + param->offsets[OFFSET_T_ENTRY],
+                                &thread->entry);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(rtos->target,
+                                ptr + param->offsets[OFFSET_T_NEXT_THREAD],
+                                &thread->next_ptr);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u32(rtos->target,
+                                ptr + param->offsets[OFFSET_T_STACK_POINTER],
+                                &thread->stack_pointer);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u8(rtos->target, ptr + param->offsets[OFFSET_T_STATE],
+                               &thread->state);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = target_read_u8(rtos->target,
+                               ptr + param->offsets[OFFSET_T_USER_OPTIONS],
+                               &thread->user_options);
+       if (retval != ERROR_OK)
+               return retval;
+
+       uint8_t prio;
+       retval = target_read_u8(rtos->target,
+                               ptr + param->offsets[OFFSET_T_PRIO], &prio);
+       if (retval != ERROR_OK)
+               return retval;
+       thread->prio = prio;
+
+       thread->name[0] = '\0';
+       if (param->offsets[OFFSET_T_NAME] != UNIMPLEMENTED) {
+               retval = target_read_buffer(rtos->target,
+                                       ptr + param->offsets[OFFSET_T_NAME],
+                                       sizeof(thread->name) - 1, (uint8_t *)thread->name);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               thread->name[sizeof(thread->name) - 1] = '\0';
+       }
+
+       LOG_DEBUG("Fetched thread%" PRIx32 ": {entry@0x%" PRIx32
+               ", state=%" PRIu8 ", useropts=%" PRIu8 ", prio=%" PRId8 "}",
+               ptr, thread->entry, thread->state, thread->user_options, thread->prio);
+
+       return ERROR_OK;
+}
+
+static int zephyr_fetch_thread_list(struct rtos *rtos, uint32_t current_thread)
+{
+       struct zephyr_array thread_array;
+       struct zephyr_thread thread;
+       struct thread_detail *td;
+       int64_t curr_id = -1;
+       uint32_t curr;
+       int retval;
+
+       retval = target_read_u32(rtos->target, zephyr_kptr(rtos, OFFSET_K_THREADS),
+               &curr);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not fetch current thread pointer");
+               return retval;
+       }
+
+       zephyr_array_init(&thread_array);
+
+       for (; curr; curr = thread.next_ptr) {
+               retval = zephyr_fetch_thread(rtos, &thread, curr);
+               if (retval != ERROR_OK)
+                       goto error;
+
+               td = zephyr_array_append(&thread_array, sizeof(*td));
+               if (!td)
+                       goto error;
+
+               td->threadid = thread.ptr;
+               td->exists = true;
+
+               if (thread.name[0])
+                       td->thread_name_str = strdup(thread.name);
+               else
+                       td->thread_name_str = alloc_printf("thr_%" PRIx32 "_%" PRIx32,
+                                                          thread.entry, thread.ptr);
+               td->extra_info_str = alloc_printf("prio:%" PRId8 ",useropts:%" PRIu8,
+                                                 thread.prio, thread.user_options);
+               if (!td->thread_name_str || !td->extra_info_str)
+                       goto error;
+
+               if (td->threadid == current_thread)
+                       curr_id = (int64_t)thread_array.elements - 1;
+       }
+
+       LOG_DEBUG("Got information for %zu threads", thread_array.elements);
+
+       rtos_free_threadlist(rtos);
+
+       rtos->thread_count = (int)thread_array.elements;
+       rtos->thread_details = zephyr_array_detach_ptr(&thread_array);
+
+       rtos->current_threadid = curr_id;
+       rtos->current_thread = current_thread;
+
+       return ERROR_OK;
+
+error:
+       td = thread_array.ptr;
+       for (size_t i = 0; i < thread_array.elements; i++) {
+               free(td[i].thread_name_str);
+               free(td[i].extra_info_str);
+       }
+
+       zephyr_array_free(&thread_array);
+
+       return ERROR_FAIL;
+}
+
+static int zephyr_update_threads(struct rtos *rtos)
+{
+       struct zephyr_params *param;
+       int retval;
+
+       if (!rtos->rtos_specific_params)
+               return ERROR_FAIL;
+
+       param = (struct zephyr_params *)rtos->rtos_specific_params;
+
+       if (!rtos->symbols) {
+               LOG_ERROR("No symbols for Zephyr");
+               return ERROR_FAIL;
+       }
+
+       if (rtos->symbols[ZEPHYR_VAL__KERNEL].address == 0) {
+               LOG_ERROR("Can't obtain kernel struct from Zephyr");
+               return ERROR_FAIL;
+       }
+
+       if (rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS].address == 0) {
+               LOG_ERROR("Please build Zephyr with CONFIG_OPENOCD option set");
+               return ERROR_FAIL;
+       }
+
+       retval = target_read_u8(rtos->target,
+               rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_SIZE_T_SIZE].address,
+               &param->size_width);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Couldn't determine size of size_t from host");
+               return retval;
+       }
+
+       if (param->size_width != 4) {
+               LOG_ERROR("Only size_t of 4 bytes are supported");
+               return ERROR_FAIL;
+       }
+
+       if (rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS].address) {
+               retval = target_read_u32(rtos->target,
+                               rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS].address,
+                               &param->num_offsets);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Couldn't not fetch number of offsets from Zephyr");
+                       return retval;
+               }
+
+               if (param->num_offsets <= OFFSET_T_STACK_POINTER) {
+                       LOG_ERROR("Number of offsets too small");
+                       return ERROR_FAIL;
+               }
+       } else {
+               retval = target_read_u32(rtos->target,
+                               rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS].address,
+                               &param->offsets[OFFSET_VERSION]);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Couldn't not fetch offsets from Zephyr");
+                       return retval;
+               }
+
+               if (param->offsets[OFFSET_VERSION] > 1) {
+                       LOG_ERROR("Unexpected OpenOCD support version %" PRIu32,
+                                       param->offsets[OFFSET_VERSION]);
+                       return ERROR_FAIL;
+               }
+               switch (param->offsets[OFFSET_VERSION]) {
+               case 0:
+                       param->num_offsets = OFFSET_T_STACK_POINTER + 1;
+                       break;
+               case 1:
+                       param->num_offsets = OFFSET_T_COOP_FLOAT + 1;
+                       break;
+               }
+       }
+       /* We can fetch the whole array for version 0, as they're supposed
+        * to grow only */
+       uint32_t address;
+       address  = rtos->symbols[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS].address;
+       for (size_t i = 0; i < OFFSET_MAX; i++, address += param->size_width) {
+               if (i >= param->num_offsets) {
+                       param->offsets[i] = UNIMPLEMENTED;
+                       continue;
+               }
+
+               retval = target_read_u32(rtos->target, address, &param->offsets[i]);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Could not fetch offsets from Zephyr");
+                       return ERROR_FAIL;
+               }
+       }
+
+       LOG_DEBUG("Zephyr OpenOCD support version %" PRId32,
+                         param->offsets[OFFSET_VERSION]);
+
+       uint32_t current_thread;
+       retval = target_read_u32(rtos->target,
+               zephyr_kptr(rtos, OFFSET_K_CURR_THREAD), &current_thread);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not obtain current thread ID");
+               return retval;
+       }
+
+       retval = zephyr_fetch_thread_list(rtos, current_thread);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Could not obtain thread list");
+               return retval;
+       }
+
+       return ERROR_OK;
+}
+
+static int zephyr_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+               struct rtos_reg **reg_list, int *num_regs)
+{
+       struct zephyr_params *params;
+       struct rtos_reg *callee_saved_reg_list = NULL;
+       target_addr_t addr;
+       int retval;
+
+       LOG_INFO("Getting thread %" PRId64 " reg list", thread_id);
+
+       if (rtos == NULL)
+               return ERROR_FAIL;
+
+       if (thread_id == 0)
+               return ERROR_FAIL;
+
+       params = rtos->rtos_specific_params;
+       if (params == NULL)
+               return ERROR_FAIL;
+
+       addr = thread_id + params->offsets[OFFSET_T_STACK_POINTER]
+                - params->callee_saved_stacking->register_offsets[0].offset;
+
+       retval = params->get_cpu_state(rtos, &addr, params, callee_saved_reg_list, reg_list, num_regs);
+
+       free(callee_saved_reg_list);
+
+       return retval;
+}
+
+static int zephyr_get_symbol_list_to_lookup(struct symbol_table_elem **symbol_list)
+{
+       *symbol_list = malloc(sizeof(zephyr_symbol_list));
+       if (!*symbol_list) {
+               LOG_ERROR("Out of memory");
+               return ERROR_FAIL;
+       }
+
+       memcpy(*symbol_list, zephyr_symbol_list, sizeof(zephyr_symbol_list));
+       return ERROR_OK;
+}
+
+struct rtos_type zephyr_rtos = {
+       .name = "Zephyr",
+
+       .detect_rtos = zephyr_detect_rtos,
+       .create = zephyr_create,
+       .update_threads = zephyr_update_threads,
+       .get_thread_reg_list = zephyr_get_thread_reg_list,
+       .get_symbol_list_to_lookup = zephyr_get_symbol_list_to_lookup,
+};