1 /*
2  * Copyright (c) 2019,2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log_backend_adsp_mtrace.h>
8 #include <zephyr/logging/log_backend.h>
9 #include <zephyr/logging/log_core.h>
10 #include <zephyr/logging/log_output.h>
11 #include <zephyr/logging/log_backend_std.h>
12 #include <zephyr/kernel.h>
13 #include <soc.h>
14 
15 #include <adsp_memory.h>
16 #include <adsp_debug_window.h>
17 
18 /*
19  * A lock is needed as log_process() and log_panic() have no internal locks
20  * to prevent concurrency. Meaning if log_process is called after
21  * log_panic was called previously, log_process may happen from another
22  * CPU and calling context than the log processing thread running in
23  * the background. On an SMP system this is a race.
24  *
25  * This caused a race on the output trace such that the logging output
26  * was garbled and useless.
27  */
28 static struct k_spinlock mtrace_lock;
29 
30 static uint32_t log_format_current = CONFIG_LOG_BACKEND_ADSP_MTRACE_OUTPUT_DEFAULT;
31 
32 static adsp_mtrace_log_hook_t mtrace_hook;
33 
34 static bool mtrace_active;
35 
36 static bool mtrace_panic_mode;
37 
38 /*
39  * SRAM window for debug info is organized to equal size slots.
40  * The descriptors on first page describe the layout of slots.
41  * One type of debug info slot is ADSP_DBG_WIN_SLOT_DEBUG_LOG.
42  * These slots (if defined) can be used for mtrace output.
43  *
44  * The log buffer slots have the following layout:
45  *
46  * u32 host_read_ptr;
47  * u32 dsp_write_ptr;
48  * u8 buffer[];
49  *
50  * The two pointers are offsets within the buffer. Buffer is empty
51  * when the two pointers are equal, and full when host read pointer
52  * is one ahead of the DSP writer pointer.
53  */
54 
55 #define MTRACE_LOG_BUF_SIZE		(ADSP_DW_SLOT_SIZE - 2 * sizeof(uint32_t))
56 
57 #define MTRACE_LOGGING_SLOT_TYPE(n)	(ADSP_DW_SLOT_DEBUG_LOG | ((n) & ADSP_DW_SLOT_CORE_MASK))
58 
59 #define MTRACE_CORE		0
60 
61 struct adsp_debug_slot {
62 	uint32_t host_ptr;
63 	uint32_t dsp_ptr;
64 	uint8_t data[ADSP_DW_SLOT_SIZE - sizeof(uint32_t) * 2];
65 } __packed;
66 
mtrace_init(void)67 static void mtrace_init(void)
68 {
69 	if (ADSP_DW->descs[ADSP_DW_SLOT_NUM_MTRACE].type == MTRACE_LOGGING_SLOT_TYPE(MTRACE_CORE)) {
70 		return;
71 	}
72 
73 	ADSP_DW->descs[ADSP_DW_SLOT_NUM_MTRACE].type = MTRACE_LOGGING_SLOT_TYPE(MTRACE_CORE);
74 }
75 
mtrace_out(int8_t * str,size_t len,size_t * space_left)76 static size_t mtrace_out(int8_t *str, size_t len, size_t *space_left)
77 {
78 	struct adsp_debug_slot *slot = (struct adsp_debug_slot *)
79 		ADSP_DW->slots[ADSP_DW_SLOT_NUM_MTRACE];
80 	uint8_t *data = slot->data;
81 	uint32_t r = slot->host_ptr;
82 	uint32_t w = slot->dsp_ptr;
83 	size_t avail, out;
84 
85 	if (w > r) {
86 		avail = MTRACE_LOG_BUF_SIZE - w + r - 1;
87 	} else if (w == r) {
88 		avail = MTRACE_LOG_BUF_SIZE - 1;
89 	} else {
90 		avail = r - w - 1;
91 	}
92 
93 	if (len == 0) {
94 		out = 0;
95 		goto out;
96 	}
97 
98 	/* data that does not fit is dropped */
99 	out = MIN(avail, len);
100 
101 	if (w + out >= MTRACE_LOG_BUF_SIZE) {
102 		size_t tail = MTRACE_LOG_BUF_SIZE - w;
103 		size_t head = out - tail;
104 
105 		memcpy(data + w, str, tail);
106 		memcpy(data, str + tail, head);
107 		w = head;
108 	} else {
109 		memcpy(data + w, str, out);
110 		w += out;
111 	}
112 
113 	slot->dsp_ptr = w;
114 
115 out:
116 	if (space_left) {
117 		*space_left = avail > len ? avail - len : 0;
118 	}
119 
120 	return out;
121 }
122 
char_out(uint8_t * data,size_t length,void * ctx)123 static int char_out(uint8_t *data, size_t length, void *ctx)
124 {
125 	size_t space_left = 0;
126 	size_t out;
127 
128 	/*
129 	 * we handle the data even if mtrace notifier is not
130 	 * active. this ensures we can capture early boot messages.
131 	 */
132 	out = mtrace_out(data, length, &space_left);
133 
134 	if (mtrace_active && mtrace_hook) {
135 
136 		/* if we are in panic mode, need to flush out asap */
137 		if (unlikely(mtrace_panic_mode)) {
138 			space_left = 0;
139 		}
140 
141 		mtrace_hook(out, space_left);
142 	}
143 
144 	return length;
145 }
146 
147 /**
148  * 80 bytes seems to catch most sensibly sized log message lines
149  * in one go letting the trace out call output whole complete
150  * lines. This avoids the overhead of a spin lock in the trace_out
151  * more often as well as avoiding entwined characters from printk if
152  * LOG_PRINTK=n.
153  */
154 #define LOG_BUF_SIZE 80
155 static uint8_t log_buf[LOG_BUF_SIZE];
156 
157 LOG_OUTPUT_DEFINE(log_output_adsp_mtrace, char_out, log_buf, sizeof(log_buf));
158 
format_flags(void)159 static uint32_t format_flags(void)
160 {
161 	uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_TIMESTAMP;
162 
163 	if (IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP)) {
164 		flags |= LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
165 	}
166 
167 	return flags;
168 }
169 
panic(struct log_backend const * const backend)170 static void panic(struct log_backend const *const backend)
171 {
172 	mtrace_panic_mode = true;
173 }
174 
dropped(const struct log_backend * const backend,uint32_t cnt)175 static void dropped(const struct log_backend *const backend,
176 		    uint32_t cnt)
177 {
178 	log_output_dropped_process(&log_output_adsp_mtrace, cnt);
179 }
180 
process(const struct log_backend * const backend,union log_msg_generic * msg)181 static void process(const struct log_backend *const backend,
182 		    union log_msg_generic *msg)
183 {
184 	log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
185 
186 	k_spinlock_key_t key = k_spin_lock(&mtrace_lock);
187 
188 	log_output_func(&log_output_adsp_mtrace, &msg->log, format_flags());
189 
190 	k_spin_unlock(&mtrace_lock, key);
191 }
192 
format_set(const struct log_backend * const backend,uint32_t log_type)193 static int format_set(const struct log_backend *const backend, uint32_t log_type)
194 {
195 	log_format_current = log_type;
196 	return 0;
197 }
198 
199 /**
200  * Lazily initialized, while the DMA may not be setup we continue
201  * to buffer log messages untilt he buffer is full.
202  */
init(const struct log_backend * const backend)203 static void init(const struct log_backend *const backend)
204 {
205 	ARG_UNUSED(backend);
206 
207 	mtrace_init();
208 }
209 
210 const struct log_backend_api log_backend_adsp_mtrace_api = {
211 	.process = process,
212 	.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
213 	.panic = panic,
214 	.format_set = format_set,
215 	.init = init,
216 };
217 
218 LOG_BACKEND_DEFINE(log_backend_adsp_mtrace, log_backend_adsp_mtrace_api, true);
219 
adsp_mtrace_log_init(adsp_mtrace_log_hook_t hook)220 void adsp_mtrace_log_init(adsp_mtrace_log_hook_t hook)
221 {
222 	mtrace_init();
223 
224 	mtrace_hook = hook;
225 	mtrace_active = !!hook;
226 }
227 
log_backend_adsp_mtrace_get(void)228 const struct log_backend *log_backend_adsp_mtrace_get(void)
229 {
230 	return &log_backend_adsp_mtrace;
231 }
232