]> Git Repo - linux.git/blob - drivers/hwtracing/stm/p_sys-t.c
Linux 6.14-rc3
[linux.git] / drivers / hwtracing / stm / p_sys-t.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * MIPI SyS-T framing protocol for STM devices.
4  * Copyright (c) 2018, Intel Corporation.
5  */
6
7 #include <linux/configfs.h>
8 #include <linux/module.h>
9 #include <linux/device.h>
10 #include <linux/slab.h>
11 #include <linux/uuid.h>
12 #include <linux/stm.h>
13 #include "stm.h"
14
15 enum sys_t_message_type {
16         MIPI_SYST_TYPE_BUILD    = 0,
17         MIPI_SYST_TYPE_SHORT32,
18         MIPI_SYST_TYPE_STRING,
19         MIPI_SYST_TYPE_CATALOG,
20         MIPI_SYST_TYPE_RAW      = 6,
21         MIPI_SYST_TYPE_SHORT64,
22         MIPI_SYST_TYPE_CLOCK,
23         MIPI_SYST_TYPE_SBD,
24 };
25
26 enum sys_t_message_severity {
27         MIPI_SYST_SEVERITY_MAX  = 0,
28         MIPI_SYST_SEVERITY_FATAL,
29         MIPI_SYST_SEVERITY_ERROR,
30         MIPI_SYST_SEVERITY_WARNING,
31         MIPI_SYST_SEVERITY_INFO,
32         MIPI_SYST_SEVERITY_USER1,
33         MIPI_SYST_SEVERITY_USER2,
34         MIPI_SYST_SEVERITY_DEBUG,
35 };
36
37 enum sys_t_message_build_subtype {
38         MIPI_SYST_BUILD_ID_COMPACT32 = 0,
39         MIPI_SYST_BUILD_ID_COMPACT64,
40         MIPI_SYST_BUILD_ID_LONG,
41 };
42
43 enum sys_t_message_clock_subtype {
44         MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1,
45 };
46
47 enum sys_t_message_string_subtype {
48         MIPI_SYST_STRING_GENERIC        = 1,
49         MIPI_SYST_STRING_FUNCTIONENTER,
50         MIPI_SYST_STRING_FUNCTIONEXIT,
51         MIPI_SYST_STRING_INVALIDPARAM   = 5,
52         MIPI_SYST_STRING_ASSERT         = 7,
53         MIPI_SYST_STRING_PRINTF_32      = 11,
54         MIPI_SYST_STRING_PRINTF_64      = 12,
55 };
56
57 /**
58  * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes
59  * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID
60  * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID
61  *
62  * Structured Binary Data messages can send information of arbitrary length,
63  * together with ID's that describe BLOB's content and layout.
64  */
65 enum sys_t_message_sbd_subtype {
66         MIPI_SYST_SBD_ID32 = 0,
67         MIPI_SYST_SBD_ID64 = 1,
68 };
69
70 #define MIPI_SYST_TYPE(t)               ((u32)(MIPI_SYST_TYPE_ ## t))
71 #define MIPI_SYST_SEVERITY(s)           ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
72 #define MIPI_SYST_OPT_LOC               BIT(8)
73 #define MIPI_SYST_OPT_LEN               BIT(9)
74 #define MIPI_SYST_OPT_CHK               BIT(10)
75 #define MIPI_SYST_OPT_TS                BIT(11)
76 #define MIPI_SYST_UNIT(u)               ((u32)(u) << 12)
77 #define MIPI_SYST_ORIGIN(o)             ((u32)(o) << 16)
78 #define MIPI_SYST_OPT_GUID              BIT(23)
79 #define MIPI_SYST_SUBTYPE(s)            ((u32)(MIPI_SYST_ ## s) << 24)
80 #define MIPI_SYST_UNITLARGE(u)          (MIPI_SYST_UNIT(u & 0xf) | \
81                                          MIPI_SYST_ORIGIN(u >> 4))
82 #define MIPI_SYST_TYPES(t, s)           (MIPI_SYST_TYPE(t) | \
83                                          MIPI_SYST_SUBTYPE(t ## _ ## s))
84
85 #define DATA_HEADER     (MIPI_SYST_TYPES(STRING, GENERIC)       | \
86                          MIPI_SYST_SEVERITY(INFO)               | \
87                          MIPI_SYST_OPT_GUID)
88
89 #define CLOCK_SYNC_HEADER       (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \
90                                  MIPI_SYST_SEVERITY(MAX))
91
92 /*
93  * SyS-T and ftrace headers are compatible to an extent that ftrace event ID
94  * and args can be treated as SyS-T SBD message with 64-bit ID and arguments
95  * BLOB right behind the header without modification. Bits [16:63] coming
96  * together with message ID from ftrace aren't used by SBD and must be zeroed.
97  *
98  *         0       15  16   23  24     31  32   39  40  63
99  * ftrace: <event_id>  <flags>  <preempt>  <-pid->  <----> <args>
100  * SBD:    <------- msg_id ------------------------------> <BLOB>
101  */
102 #define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \
103                          MIPI_SYST_SEVERITY(INFO)               | \
104                          MIPI_SYST_OPT_GUID)
105
106 struct sys_t_policy_node {
107         uuid_t          uuid;
108         bool            do_len;
109         unsigned long   ts_interval;
110         unsigned long   clocksync_interval;
111 };
112
113 struct sys_t_output {
114         struct sys_t_policy_node        node;
115         unsigned long   ts_jiffies;
116         unsigned long   clocksync_jiffies;
117 };
118
119 static void sys_t_policy_node_init(void *priv)
120 {
121         struct sys_t_policy_node *pn = priv;
122
123         uuid_gen(&pn->uuid);
124 }
125
126 static int sys_t_output_open(void *priv, struct stm_output *output)
127 {
128         struct sys_t_policy_node *pn = priv;
129         struct sys_t_output *opriv;
130
131         opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
132         if (!opriv)
133                 return -ENOMEM;
134
135         memcpy(&opriv->node, pn, sizeof(opriv->node));
136         output->pdrv_private = opriv;
137
138         return 0;
139 }
140
141 static void sys_t_output_close(struct stm_output *output)
142 {
143         kfree(output->pdrv_private);
144 }
145
146 static ssize_t sys_t_policy_uuid_show(struct config_item *item,
147                                       char *page)
148 {
149         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
150
151         return sprintf(page, "%pU\n", &pn->uuid);
152 }
153
154 static ssize_t
155 sys_t_policy_uuid_store(struct config_item *item, const char *page,
156                         size_t count)
157 {
158         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
159         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
160         int ret;
161
162         mutex_lock(mutexp);
163         ret = uuid_parse(page, &pn->uuid);
164         mutex_unlock(mutexp);
165
166         return ret < 0 ? ret : count;
167 }
168
169 CONFIGFS_ATTR(sys_t_policy_, uuid);
170
171 static ssize_t sys_t_policy_do_len_show(struct config_item *item,
172                                       char *page)
173 {
174         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
175
176         return sprintf(page, "%d\n", pn->do_len);
177 }
178
179 static ssize_t
180 sys_t_policy_do_len_store(struct config_item *item, const char *page,
181                         size_t count)
182 {
183         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
184         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
185         int ret;
186
187         mutex_lock(mutexp);
188         ret = kstrtobool(page, &pn->do_len);
189         mutex_unlock(mutexp);
190
191         return ret ? ret : count;
192 }
193
194 CONFIGFS_ATTR(sys_t_policy_, do_len);
195
196 static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
197                                              char *page)
198 {
199         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
200
201         return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
202 }
203
204 static ssize_t
205 sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
206                                size_t count)
207 {
208         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
209         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
210         unsigned int ms;
211         int ret;
212
213         mutex_lock(mutexp);
214         ret = kstrtouint(page, 10, &ms);
215         mutex_unlock(mutexp);
216
217         if (!ret) {
218                 pn->ts_interval = msecs_to_jiffies(ms);
219                 return count;
220         }
221
222         return ret;
223 }
224
225 CONFIGFS_ATTR(sys_t_policy_, ts_interval);
226
227 static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item,
228                                                     char *page)
229 {
230         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
231
232         return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval));
233 }
234
235 static ssize_t
236 sys_t_policy_clocksync_interval_store(struct config_item *item,
237                                       const char *page, size_t count)
238 {
239         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
240         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
241         unsigned int ms;
242         int ret;
243
244         mutex_lock(mutexp);
245         ret = kstrtouint(page, 10, &ms);
246         mutex_unlock(mutexp);
247
248         if (!ret) {
249                 pn->clocksync_interval = msecs_to_jiffies(ms);
250                 return count;
251         }
252
253         return ret;
254 }
255
256 CONFIGFS_ATTR(sys_t_policy_, clocksync_interval);
257
258 static struct configfs_attribute *sys_t_policy_attrs[] = {
259         &sys_t_policy_attr_uuid,
260         &sys_t_policy_attr_do_len,
261         &sys_t_policy_attr_ts_interval,
262         &sys_t_policy_attr_clocksync_interval,
263         NULL,
264 };
265
266 static inline bool sys_t_need_ts(struct sys_t_output *op)
267 {
268         if (op->node.ts_interval &&
269             time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) {
270                 op->ts_jiffies = jiffies;
271
272                 return true;
273         }
274
275         return false;
276 }
277
278 static bool sys_t_need_clock_sync(struct sys_t_output *op)
279 {
280         if (op->node.clocksync_interval &&
281             time_after(jiffies,
282                        op->clocksync_jiffies + op->node.clocksync_interval)) {
283                 op->clocksync_jiffies = jiffies;
284
285                 return true;
286         }
287
288         return false;
289 }
290
291 static ssize_t
292 sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c)
293 {
294         u32 header = CLOCK_SYNC_HEADER;
295         const unsigned char nil = 0;
296         u64 payload[2]; /* Clock value and frequency */
297         ssize_t sz;
298
299         sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
300                           4, (u8 *)&header);
301         if (sz <= 0)
302                 return sz;
303
304         payload[0] = ktime_get_real_ns();
305         payload[1] = NSEC_PER_SEC;
306         sz = stm_data_write(data, m, c, false, &payload, sizeof(payload));
307         if (sz <= 0)
308                 return sz;
309
310         data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
311
312         return sizeof(header) + sizeof(payload);
313 }
314
315 static inline u32 sys_t_header(struct stm_source_data *source)
316 {
317         if (source && source->type == STM_FTRACE)
318                 return SBD_HEADER;
319         return DATA_HEADER;
320 }
321
322 static ssize_t sys_t_write_data(struct stm_data *data,
323                                 struct stm_source_data *source,
324                                 unsigned int master, unsigned int channel,
325                                 bool ts_first, const void *buf, size_t count)
326 {
327         ssize_t sz;
328         const unsigned char nil = 0;
329
330         /*
331          * Ftrace is zero-copy compatible with SyS-T SBD, but requires
332          * special handling of first 64 bits. Trim and send them separately
333          * to avoid damage on original ftrace buffer.
334          */
335         if (source && source->type == STM_FTRACE) {
336                 u64 compat_ftrace_header;
337                 ssize_t header_sz;
338                 ssize_t buf_sz;
339
340                 if (count < sizeof(compat_ftrace_header))
341                         return -EINVAL;
342
343                 /* SBD only makes use of low 16 bits (event ID) from ftrace event */
344                 compat_ftrace_header = *(u64 *)buf & 0xffff;
345                 header_sz = stm_data_write(data, master, channel, false,
346                                            &compat_ftrace_header,
347                                            sizeof(compat_ftrace_header));
348                 if (header_sz != sizeof(compat_ftrace_header))
349                         return header_sz;
350
351                 buf_sz = stm_data_write(data, master, channel, false,
352                                         buf + header_sz, count - header_sz);
353                 if (buf_sz != count - header_sz)
354                         return buf_sz;
355                 sz = header_sz + buf_sz;
356         } else {
357                 sz = stm_data_write(data, master, channel, false, buf, count);
358         }
359
360         if (sz <= 0)
361                 return sz;
362
363         data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
364
365         return sz;
366 }
367
368 static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
369                            unsigned int chan, const char *buf, size_t count,
370                            struct stm_source_data *source)
371 {
372         struct sys_t_output *op = output->pdrv_private;
373         unsigned int c = output->channel + chan;
374         unsigned int m = output->master;
375         u32 header = sys_t_header(source);
376         u8 uuid[UUID_SIZE];
377         ssize_t sz;
378
379         /* We require an existing policy node to proceed */
380         if (!op)
381                 return -EINVAL;
382
383         if (sys_t_need_clock_sync(op)) {
384                 sz = sys_t_clock_sync(data, m, c);
385                 if (sz <= 0)
386                         return sz;
387         }
388
389         if (op->node.do_len)
390                 header |= MIPI_SYST_OPT_LEN;
391         if (sys_t_need_ts(op))
392                 header |= MIPI_SYST_OPT_TS;
393
394         /*
395          * STP framing rules for SyS-T frames:
396          *   * the first packet of the SyS-T frame is timestamped;
397          *   * the last packet is a FLAG.
398          */
399         /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
400         /* HEADER */
401         sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
402                           4, (u8 *)&header);
403         if (sz <= 0)
404                 return sz;
405
406         /* GUID */
407         export_uuid(uuid, &op->node.uuid);
408         sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid));
409         if (sz <= 0)
410                 return sz;
411
412         /* [LENGTH] */
413         if (op->node.do_len) {
414                 u16 length = count;
415
416                 sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
417                                   (u8 *)&length);
418                 if (sz <= 0)
419                         return sz;
420         }
421
422         /* [TIMESTAMP] */
423         if (header & MIPI_SYST_OPT_TS) {
424                 u64 ts = ktime_get_real_ns();
425
426                 sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
427                 if (sz <= 0)
428                         return sz;
429         }
430
431         /* DATA */
432         return sys_t_write_data(data, source, m, c, false, buf, count);
433 }
434
435 static const struct stm_protocol_driver sys_t_pdrv = {
436         .owner                  = THIS_MODULE,
437         .name                   = "p_sys-t",
438         .priv_sz                = sizeof(struct sys_t_policy_node),
439         .write                  = sys_t_write,
440         .policy_attr            = sys_t_policy_attrs,
441         .policy_node_init       = sys_t_policy_node_init,
442         .output_open            = sys_t_output_open,
443         .output_close           = sys_t_output_close,
444 };
445
446 static int sys_t_stm_init(void)
447 {
448         return stm_register_protocol(&sys_t_pdrv);
449 }
450
451 static void sys_t_stm_exit(void)
452 {
453         stm_unregister_protocol(&sys_t_pdrv);
454 }
455
456 module_init(sys_t_stm_init);
457 module_exit(sys_t_stm_exit);
458
459 MODULE_LICENSE("GPL v2");
460 MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");
461 MODULE_AUTHOR("Alexander Shishkin <[email protected]>");
This page took 0.054768 seconds and 4 git commands to generate.