1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2020 Intel Corporation. All rights reserved.
4  *
5  * Author: Karol Trzcinski	<karolx.trzcinski@linux.intel.com>
6  */
7 
8 #include <ipc/trace.h>
9 #include <smex/ldc.h>
10 #include <sof/lib/uuid.h>
11 #include <sof/list.h>
12 #include <user/trace.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include "convert.h"
19 #include "filter.h"
20 #include "misc.h"
21 
22 #define COMPONENTS_SEPARATOR ','
23 #define COMPONENT_NAME_SCAN_STRING_LENGTH 32
24 
25 extern struct convert_config *global_config;
26 
27 /** map between log level given by user and enum value */
28 static const struct {
29 	const char name[16];
30 	int32_t log_level;
31 } log_level_dict[] = {
32 	{"verbose",	LOG_LEVEL_VERBOSE},
33 	{"debug",	LOG_LEVEL_DEBUG},
34 	{"info",	LOG_LEVEL_INFO},
35 	{"warning",	LOG_LEVEL_WARNING},
36 	{"error",	LOG_LEVEL_ERROR},
37 	{"critical",	LOG_LEVEL_CRITICAL},
38 	{"v",		LOG_LEVEL_VERBOSE},
39 	{"d",		LOG_LEVEL_DEBUG},
40 	{"i",		LOG_LEVEL_INFO},
41 	{"w",		LOG_LEVEL_WARNING},
42 	{"e",		LOG_LEVEL_ERROR},
43 	{"c",		LOG_LEVEL_CRITICAL},
44 };
45 
46 struct filter_element {
47 	struct list_item list;
48 	int32_t uuid_id;	/**< type id, or -1 when not important */
49 	int32_t comp_id;	/**< component id or -1 when not important */
50 	int32_t pipe_id;	/**< pipeline id or -1 when not important */
51 	int32_t log_level;	/**< new log level value */
52 };
53 
54 /**
55  * Search for uuid entry with given component name in given uids dictionary
56  * @param name of uuid entry
57  * @return pointer to sof_uuid_entry with given name
58  */
get_uuid_by_name(const char * name)59 static struct sof_uuid_entry *get_uuid_by_name(const char *name)
60 {
61 	const struct snd_sof_uids_header *uids_dict = global_config->uids_dict;
62 	uintptr_t beg = (uintptr_t)uids_dict + uids_dict->data_offset;
63 	uintptr_t end = beg + uids_dict->data_length;
64 	struct sof_uuid_entry *ptr = (struct sof_uuid_entry *)beg;
65 
66 	while ((uintptr_t)ptr < end) {
67 		if (strcmp(name, ptr->name) == 0)
68 			return ptr;
69 		++ptr;
70 	}
71 	return NULL;
72 }
73 
74 /**
75  * Parse log level name (from log_level_dict) to enum value.
76  * Take care about possible whitespace at the begin.
77  * @param value_start pointer to the begin of range to search
78  * @return enum value for given log level, or -1 for invalid value
79  */
filter_parse_log_level(const char * value_start)80 static int filter_parse_log_level(const char *value_start)
81 {
82 	int i;
83 
84 	for (i = 0; i < ARRAY_SIZE(log_level_dict); ++i) {
85 		if (!strcmp(log_level_dict[i].name, value_start))
86 			return log_level_dict[i].log_level;
87 	}
88 	return -1;
89 }
90 
filter_parse_component_name(char * input_str,struct filter_element * out)91 static char *filter_parse_component_name(char *input_str, struct filter_element *out)
92 {
93 	static char scan_format_string[COMPONENT_NAME_SCAN_STRING_LENGTH] = "";
94 	char comp_name[UUID_NAME_MAX_LEN];
95 	struct sof_uuid_entry *uuid_entry;
96 	int ret;
97 
98 	/* if component name is not specified, stay with default out->uuid_id value */
99 	if (input_str[0] == '*')
100 		return &input_str[1];
101 
102 	/*
103 	 * Take care about buffer overflows when dealing with input from
104 	 * user, so scan no more than UUID_NAME_MAX_LEN bytes to
105 	 * `comp_name` variable. Only once initialise scan_format_string.
106 	 */
107 	if (strlen(scan_format_string) == 0) {
108 		ret = snprintf(scan_format_string, sizeof(scan_format_string),
109 				"%%%d[^0-9* ]s", UUID_NAME_MAX_LEN);
110 		if (ret <= 0)
111 			return NULL;
112 	}
113 	ret = sscanf(input_str, scan_format_string, comp_name);
114 	if (ret <= 0)
115 		return NULL;
116 
117 	/* find component uuid key */
118 	uuid_entry = get_uuid_by_name(comp_name);
119 	if (!uuid_entry) {
120 		log_err("unknown component name `%s`\n", comp_name);
121 		return NULL;
122 	}
123 	out->uuid_id = get_uuid_key(uuid_entry);
124 	return strstr(input_str, comp_name) + strlen(comp_name);
125 }
126 
127 /**
128  * Parse component definition from input_str.
129  *
130  * Possible input_str formats:
131  *   `name pipe_id.comp_id`
132  *   `name pipe_id.*`
133  *   `name *`
134  *   `name`
135  *   `* pipe_id.comp_id`
136  *   `* pipe_id.*`
137  *   `*`
138  * Whitespace is possible at the begin, end and after `name`.
139  * `name` must refer to values from given UUID dictionary,
140  *        (so name comes from DECLARE_SOF_UUID macro usage)
141 
142  * @param input_str formatted component definition
143  * @param out element where component definition should be saved
144  */
filter_parse_component(char * input_str,struct filter_element * out)145 static int filter_parse_component(char *input_str, struct filter_element *out)
146 {
147 	char *instance_info;
148 	int ret;
149 
150 	/* trim whitespaces, to easily check first and last char */
151 	input_str = trim(input_str);
152 
153 	/* assign default values */
154 	out->uuid_id = 0;
155 	out->pipe_id = -1;
156 	out->comp_id = -1;
157 
158 	/* parse component name and store pointer after component name, pointer to instance info */
159 	instance_info = filter_parse_component_name(input_str, out);
160 	if (!instance_info) {
161 		log_err("component name parsing `%s`\n", input_str);
162 		return -EINVAL;
163 	}
164 
165 	/* if instance is not specified then stop parsing */
166 	instance_info = ltrim(instance_info);
167 	if (instance_info[0] == '\0' ||
168 	    (instance_info[0] == '*' && instance_info[1] == '\0')) {
169 		return 0;
170 	}
171 
172 	/* now parse last part: `number.x` where x is a number or `*` */
173 	ret = sscanf(instance_info, "%d.%d", &out->pipe_id, &out->comp_id);
174 	if (ret == 2)
175 		return 0;
176 	else if (ret != 1)
177 		return -EINVAL;
178 
179 	/* pipeline id parsed but component id is not a number */
180 	if (instance_info[strlen(instance_info) - 1] == '*')
181 		return 0;
182 	log_err("Use * to specify each component on particular pipeline\n");
183 	return -EINVAL;
184 }
185 
186 /**
187  * Convert argument from -F flag to sof_ipc_dma_trace_filter_elem struct values.
188  *
189  * Possible log_level - see filter_parse_log_level() documentation
190  * Possible component - list of components separated by `COMPONENTS_SEPARATOR`,
191  *                      for single component definition description look at
192  *                      filter_parse_component() documentation
193  *
194  * Examples:
195  *   `debug="pipe1"` - set debug log level for components from pipeline1
196  *   `d="pipe1, dai2.3"` - as above, but also for dai2.3
197  *   `error="FIR*"` - for each FIR component set log level to error
198  *
199  * @param input_str log level settings in format `log_level=component`
200  * @param out_list output list with filter_element elements
201  */
filter_parse_entry(char * input_str,struct list_item * out_list)202 static int filter_parse_entry(char *input_str, struct list_item *out_list)
203 {
204 	struct filter_element *filter;
205 	char *comp_fmt_end;
206 	int32_t log_level;
207 	char *comp_fmt;
208 	int ret;
209 
210 	/*
211 	 * split string on '=' char, left part describes log level,
212 	 * the right one is for component description.
213 	 */
214 	comp_fmt = strchr(input_str, '=');
215 	if (!comp_fmt) {
216 		log_err("unable to find `=` in `%s`\n", input_str);
217 		return -EINVAL;
218 	}
219 	*comp_fmt = 0;
220 	++comp_fmt;
221 
222 	/* find correct log level in given conf string - string before `=` */
223 	log_level = filter_parse_log_level(input_str);
224 	if (log_level < 0) {
225 		log_err("unable to parse log level from `%s`\n", input_str);
226 		return log_level;
227 	}
228 
229 	/*
230 	 * now parse list of components name and optional instance identifier,
231 	 * split string on `COMPONENTS_SEPARATOR`
232 	 */
233 	while (comp_fmt) {
234 		filter = malloc(sizeof(struct filter_element));
235 		if (!filter) {
236 			log_err("unable to malloc memory\n");
237 			return -ENOMEM;
238 		}
239 
240 		comp_fmt_end = strchr(comp_fmt, COMPONENTS_SEPARATOR);
241 		if (comp_fmt_end)
242 			*comp_fmt_end = '\0';
243 		ret = filter_parse_component(comp_fmt, filter);
244 		if (ret < 0) {
245 			log_err("unable to parse component from `%s`\n", comp_fmt);
246 			free(filter);
247 			return ret;
248 		}
249 		filter->log_level = log_level;
250 
251 		list_item_append(&filter->list, out_list);
252 		comp_fmt = comp_fmt_end ? comp_fmt_end + 1 : 0;
253 	}
254 
255 	return 0;
256 }
257 
258 /**
259  * Parse `input_str` content and send it to FW via debugFS.
260  *
261  * `input_str` contain single filter definition element per line.
262  * Each line is parsed by `filter_parse_entry`, and saved in list.
263  * List of `sof_ipc_dma_trace_filter_elem` is writend to debugFS,
264  * and then send as IPC to FW (this action is implemented in driver).
265  * Each line in debugFS represents single IPC message.
266  */
filter_update_firmware(void)267 int filter_update_firmware(void)
268 {
269 	char *input_str = global_config->filter_config;
270 	struct filter_element *filter;
271 	struct list_item filter_list;
272 	struct list_item *list_elem;
273 	struct list_item *list_temp;
274 	char *line_end;
275 	FILE *out_fd = NULL;
276 	int ret = 0;
277 
278 	list_init(&filter_list);
279 
280 	/* parse `input_str` line by line */
281 	line_end = strchr(input_str, '\n');
282 	while (line_end) {
283 		line_end[0] = '\0';
284 		ret = filter_parse_entry(input_str, &filter_list);
285 		if (ret < 0)
286 			goto err;
287 		input_str = line_end + 1;
288 		line_end = strchr(input_str, '\n');
289 	}
290 
291 	/* write output to debugFS */
292 	out_fd = fopen(FILTER_KERNEL_PATH, "w");
293 	if (!out_fd) {
294 		log_err("Unable to open out file '%s'\n", FILTER_KERNEL_PATH);
295 		ret = -errno;
296 		goto err;
297 	}
298 
299 	list_for_item(list_elem, &filter_list) {
300 		filter = container_of(list_elem, struct filter_element, list);
301 		fprintf(out_fd, "%d %X %d %d;", filter->log_level,
302 			filter->uuid_id, filter->pipe_id, filter->comp_id);
303 	}
304 	fprintf(out_fd, "\n");
305 
306 err:
307 	if (out_fd)
308 		fclose(out_fd);
309 
310 	/* free each component from parsed element list */
311 	list_for_item_safe(list_elem, list_temp, &filter_list) {
312 		filter = container_of(list_elem, struct filter_element, list);
313 		free(filter);
314 	}
315 
316 	return ret;
317 }
318