1 /*
2  * Copyright (c) 2020 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <kernel_internal.h>
9 #include <zephyr/toolchain.h>
10 #include <zephyr/debug/coredump.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/util.h>
13 
14 #include "coredump_internal.h"
15 #if defined(CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING)
16 extern struct coredump_backend_api coredump_backend_logging;
17 static struct coredump_backend_api
18 	*backend_api = &coredump_backend_logging;
19 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_FLASH_PARTITION)
20 extern struct coredump_backend_api coredump_backend_flash_partition;
21 static struct coredump_backend_api
22 	*backend_api = &coredump_backend_flash_partition;
23 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_INTEL_ADSP_MEM_WINDOW)
24 extern struct coredump_backend_api coredump_backend_intel_adsp_mem_window;
25 static struct coredump_backend_api
26 	*backend_api = &coredump_backend_intel_adsp_mem_window;
27 #elif defined(CONFIG_DEBUG_COREDUMP_BACKEND_OTHER)
28 extern struct coredump_backend_api coredump_backend_other;
29 static struct coredump_backend_api
30 	*backend_api = &coredump_backend_other;
31 #else
32 #error "Need to select a coredump backend"
33 #endif
34 
35 #if defined(CONFIG_COREDUMP_DEVICE)
36 #include <zephyr/drivers/coredump.h>
37 #define DT_DRV_COMPAT zephyr_coredump
38 #endif
39 
dump_header(unsigned int reason)40 static void dump_header(unsigned int reason)
41 {
42 	struct coredump_hdr_t hdr = {
43 		.id = {'Z', 'E'},
44 		.hdr_version = COREDUMP_HDR_VER,
45 		.reason = sys_cpu_to_le16(reason),
46 	};
47 
48 	if (sizeof(uintptr_t) == 8) {
49 		hdr.ptr_size_bits = 6; /* 2^6 = 64 */
50 	} else if (sizeof(uintptr_t) == 4) {
51 		hdr.ptr_size_bits = 5; /* 2^5 = 32 */
52 	} else {
53 		hdr.ptr_size_bits = 0; /* Unknown */
54 	}
55 
56 	hdr.tgt_code = sys_cpu_to_le16(arch_coredump_tgt_code_get());
57 
58 	backend_api->buffer_output((uint8_t *)&hdr, sizeof(hdr));
59 }
60 
dump_thread(struct k_thread * thread)61 static void dump_thread(struct k_thread *thread)
62 {
63 #ifdef CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN
64 	uintptr_t end_addr;
65 
66 	/*
67 	 * When dumping minimum information,
68 	 * the current thread struct and stack need to
69 	 * to be dumped so debugger can examine them.
70 	 */
71 
72 	if (thread == NULL) {
73 		return;
74 	}
75 
76 	end_addr = POINTER_TO_UINT(thread) + sizeof(*thread);
77 
78 	coredump_memory_dump(POINTER_TO_UINT(thread), end_addr);
79 
80 	end_addr = thread->stack_info.start + thread->stack_info.size;
81 
82 	coredump_memory_dump(thread->stack_info.start, end_addr);
83 #endif
84 }
85 
86 #if defined(CONFIG_COREDUMP_DEVICE)
process_coredump_dev_memory(const struct device * dev)87 static void process_coredump_dev_memory(const struct device *dev)
88 {
89 	struct coredump_driver_api *api = (struct coredump_driver_api *)dev->api;
90 
91 	api->dump(dev);
92 }
93 #endif
94 
process_memory_region_list(void)95 void process_memory_region_list(void)
96 {
97 #ifdef CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_LINKER_RAM
98 	unsigned int idx = 0;
99 
100 	while (true) {
101 		struct z_coredump_memory_region_t *r =
102 			&z_coredump_memory_regions[idx];
103 
104 		if (r->end == POINTER_TO_UINT(NULL)) {
105 			break;
106 		}
107 
108 		coredump_memory_dump(r->start, r->end);
109 
110 		idx++;
111 	}
112 #endif
113 
114 #if defined(CONFIG_COREDUMP_DEVICE)
115 #define MY_FN(inst) process_coredump_dev_memory(DEVICE_DT_INST_GET(inst));
116 	DT_INST_FOREACH_STATUS_OKAY(MY_FN)
117 #endif
118 }
119 
coredump(unsigned int reason,const z_arch_esf_t * esf,struct k_thread * thread)120 void coredump(unsigned int reason, const z_arch_esf_t *esf,
121 	      struct k_thread *thread)
122 {
123 	z_coredump_start();
124 
125 	dump_header(reason);
126 
127 	if (esf != NULL) {
128 		arch_coredump_info_dump(esf);
129 	}
130 
131 	if (thread != NULL) {
132 		dump_thread(thread);
133 	}
134 
135 	process_memory_region_list();
136 
137 	z_coredump_end();
138 }
139 
z_coredump_start(void)140 void z_coredump_start(void)
141 {
142 	backend_api->start();
143 }
144 
z_coredump_end(void)145 void z_coredump_end(void)
146 {
147 	backend_api->end();
148 }
149 
coredump_buffer_output(uint8_t * buf,size_t buflen)150 void coredump_buffer_output(uint8_t *buf, size_t buflen)
151 {
152 	if ((buf == NULL) || (buflen == 0)) {
153 		/* Invalid buffer, skip */
154 		return;
155 	}
156 
157 	backend_api->buffer_output(buf, buflen);
158 }
159 
coredump_memory_dump(uintptr_t start_addr,uintptr_t end_addr)160 void coredump_memory_dump(uintptr_t start_addr, uintptr_t end_addr)
161 {
162 	struct coredump_mem_hdr_t m;
163 	size_t len;
164 
165 	if ((start_addr == POINTER_TO_UINT(NULL)) ||
166 	    (end_addr == POINTER_TO_UINT(NULL))) {
167 		return;
168 	}
169 
170 	if (start_addr >= end_addr) {
171 		return;
172 	}
173 
174 	len = end_addr - start_addr;
175 
176 	m.id = COREDUMP_MEM_HDR_ID;
177 	m.hdr_version = COREDUMP_MEM_HDR_VER;
178 
179 	if (sizeof(uintptr_t) == 8) {
180 		m.start	= sys_cpu_to_le64(start_addr);
181 		m.end = sys_cpu_to_le64(end_addr);
182 	} else if (sizeof(uintptr_t) == 4) {
183 		m.start	= sys_cpu_to_le32(start_addr);
184 		m.end = sys_cpu_to_le32(end_addr);
185 	}
186 
187 	coredump_buffer_output((uint8_t *)&m, sizeof(m));
188 
189 	coredump_buffer_output((uint8_t *)start_addr, len);
190 }
191 
coredump_query(enum coredump_query_id query_id,void * arg)192 int coredump_query(enum coredump_query_id query_id, void *arg)
193 {
194 	int ret;
195 
196 	if (backend_api->query == NULL) {
197 		ret = -ENOTSUP;
198 	} else {
199 		ret = backend_api->query(query_id, arg);
200 	}
201 
202 	return ret;
203 }
204 
coredump_cmd(enum coredump_cmd_id cmd_id,void * arg)205 int coredump_cmd(enum coredump_cmd_id cmd_id, void *arg)
206 {
207 	int ret;
208 
209 	if (backend_api->cmd == NULL) {
210 		ret = -ENOTSUP;
211 	} else {
212 		ret = backend_api->cmd(cmd_id, arg);
213 	}
214 
215 	return ret;
216 }
217