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[0].type == MTRACE_LOGGING_SLOT_TYPE(MTRACE_CORE)) {
70 return;
71 }
72
73 ADSP_DW->descs[0].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 *)(ADSP_DW->slots[0]);
79 uint8_t *data = slot->data;
80 uint32_t r = slot->host_ptr;
81 uint32_t w = slot->dsp_ptr;
82 size_t avail, out;
83
84 if (w > r) {
85 avail = MTRACE_LOG_BUF_SIZE - w + r - 1;
86 } else if (w == r) {
87 avail = MTRACE_LOG_BUF_SIZE - 1;
88 } else {
89 avail = r - w - 1;
90 }
91
92 if (len == 0) {
93 out = 0;
94 goto out;
95 }
96
97 /* data that does not fit is dropped */
98 out = MIN(avail, len);
99
100 if (w + out >= MTRACE_LOG_BUF_SIZE) {
101 size_t tail = MTRACE_LOG_BUF_SIZE - w;
102 size_t head = out - tail;
103
104 memcpy(data + w, str, tail);
105 memcpy(data, str + tail, head);
106 w = head;
107 } else {
108 memcpy(data + w, str, out);
109 w += out;
110 }
111
112 slot->dsp_ptr = w;
113
114 out:
115 if (space_left) {
116 *space_left = avail > len ? avail - len : 0;
117 }
118
119 return out;
120 }
121
char_out(uint8_t * data,size_t length,void * ctx)122 static int char_out(uint8_t *data, size_t length, void *ctx)
123 {
124 size_t space_left = 0;
125 size_t out;
126
127 /*
128 * we handle the data even if mtrace notifier is not
129 * active. this ensures we can capture early boot messages.
130 */
131 out = mtrace_out(data, length, &space_left);
132
133 if (mtrace_active && mtrace_hook) {
134
135 /* if we are in panic mode, need to flush out asap */
136 if (unlikely(mtrace_panic_mode)) {
137 space_left = 0;
138 }
139
140 mtrace_hook(out, space_left);
141 }
142
143 return length;
144 }
145
146 /**
147 * 80 bytes seems to catch most sensibly sized log message lines
148 * in one go letting the trace out call output whole complete
149 * lines. This avoids the overhead of a spin lock in the trace_out
150 * more often as well as avoiding entwined characters from printk if
151 * LOG_PRINTK=n.
152 */
153 #define LOG_BUF_SIZE 80
154 static uint8_t log_buf[LOG_BUF_SIZE];
155
156 LOG_OUTPUT_DEFINE(log_output_adsp_mtrace, char_out, log_buf, sizeof(log_buf));
157
format_flags(void)158 static uint32_t format_flags(void)
159 {
160 uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_TIMESTAMP;
161
162 if (IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP)) {
163 flags |= LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP;
164 }
165
166 return flags;
167 }
168
panic(struct log_backend const * const backend)169 static void panic(struct log_backend const *const backend)
170 {
171 mtrace_panic_mode = true;
172 }
173
dropped(const struct log_backend * const backend,uint32_t cnt)174 static void dropped(const struct log_backend *const backend,
175 uint32_t cnt)
176 {
177 log_output_dropped_process(&log_output_adsp_mtrace, cnt);
178 }
179
process(const struct log_backend * const backend,union log_msg_generic * msg)180 static void process(const struct log_backend *const backend,
181 union log_msg_generic *msg)
182 {
183 log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
184
185 k_spinlock_key_t key = k_spin_lock(&mtrace_lock);
186
187 log_output_func(&log_output_adsp_mtrace, &msg->log, format_flags());
188
189 k_spin_unlock(&mtrace_lock, key);
190 }
191
format_set(const struct log_backend * const backend,uint32_t log_type)192 static int format_set(const struct log_backend *const backend, uint32_t log_type)
193 {
194 log_format_current = log_type;
195 return 0;
196 }
197
198 /**
199 * Lazily initialized, while the DMA may not be setup we continue
200 * to buffer log messages untilt he buffer is full.
201 */
init(const struct log_backend * const backend)202 static void init(const struct log_backend *const backend)
203 {
204 ARG_UNUSED(backend);
205
206 mtrace_init();
207 }
208
209 const struct log_backend_api log_backend_adsp_mtrace_api = {
210 .process = process,
211 .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
212 .panic = panic,
213 .format_set = format_set,
214 .init = init,
215 };
216
217 LOG_BACKEND_DEFINE(log_backend_adsp_mtrace, log_backend_adsp_mtrace_api, true);
218
adsp_mtrace_log_init(adsp_mtrace_log_hook_t hook)219 void adsp_mtrace_log_init(adsp_mtrace_log_hook_t hook)
220 {
221 mtrace_init();
222
223 mtrace_hook = hook;
224 mtrace_active = true;
225 }
226
log_backend_adsp_mtrace_get(void)227 const struct log_backend *log_backend_adsp_mtrace_get(void)
228 {
229 return &log_backend_adsp_mtrace;
230 }
231