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