162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ptrace.h>
462306a36Sopenharmony_ci#include <stddef.h>
562306a36Sopenharmony_ci#include <linux/bpf.h>
662306a36Sopenharmony_ci#include <bpf/bpf_helpers.h>
762306a36Sopenharmony_ci#include <bpf/bpf_tracing.h>
862306a36Sopenharmony_ci#include "bpf_misc.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cichar _license[] SEC("license") = "GPL";
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* typically virtio scsi has max SGs of 6 */
1362306a36Sopenharmony_ci#define VIRTIO_MAX_SGS	6
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Verifier will fail with SG_MAX = 128. The failure can be
1662306a36Sopenharmony_ci * workarounded with a smaller SG_MAX, e.g. 10.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci#define WORKAROUND
1962306a36Sopenharmony_ci#ifdef WORKAROUND
2062306a36Sopenharmony_ci#define SG_MAX		10
2162306a36Sopenharmony_ci#else
2262306a36Sopenharmony_ci/* typically virtio blk has max SEG of 128 */
2362306a36Sopenharmony_ci#define SG_MAX		128
2462306a36Sopenharmony_ci#endif
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define SG_CHAIN	0x01UL
2762306a36Sopenharmony_ci#define SG_END		0x02UL
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct scatterlist {
3062306a36Sopenharmony_ci	unsigned long   page_link;
3162306a36Sopenharmony_ci	unsigned int    offset;
3262306a36Sopenharmony_ci	unsigned int    length;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define sg_is_chain(sg)		((sg)->page_link & SG_CHAIN)
3662306a36Sopenharmony_ci#define sg_is_last(sg)		((sg)->page_link & SG_END)
3762306a36Sopenharmony_ci#define sg_chain_ptr(sg)	\
3862306a36Sopenharmony_ci	((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline struct scatterlist *__sg_next(struct scatterlist *sgp)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct scatterlist sg;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
4562306a36Sopenharmony_ci	if (sg_is_last(&sg))
4662306a36Sopenharmony_ci		return NULL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	sgp++;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
5162306a36Sopenharmony_ci	if (sg_is_chain(&sg))
5262306a36Sopenharmony_ci		sgp = sg_chain_ptr(&sg);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return sgp;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline struct scatterlist *get_sgp(struct scatterlist **sgs, int i)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct scatterlist *sgp;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	bpf_probe_read_kernel(&sgp, sizeof(sgp), sgs + i);
6262306a36Sopenharmony_ci	return sgp;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint config = 0;
6662306a36Sopenharmony_ciint result = 0;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciSEC("kprobe/virtqueue_add_sgs")
6962306a36Sopenharmony_ciint BPF_KPROBE(trace_virtqueue_add_sgs, void *unused, struct scatterlist **sgs,
7062306a36Sopenharmony_ci	       unsigned int out_sgs, unsigned int in_sgs)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct scatterlist *sgp = NULL;
7362306a36Sopenharmony_ci	__u64 length1 = 0, length2 = 0;
7462306a36Sopenharmony_ci	unsigned int i, n, len;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (config != 0)
7762306a36Sopenharmony_ci		return 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	for (i = 0; (i < VIRTIO_MAX_SGS) && (i < out_sgs); i++) {
8062306a36Sopenharmony_ci		__sink(out_sgs);
8162306a36Sopenharmony_ci		for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
8262306a36Sopenharmony_ci		     sgp = __sg_next(sgp)) {
8362306a36Sopenharmony_ci			bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
8462306a36Sopenharmony_ci			length1 += len;
8562306a36Sopenharmony_ci			n++;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	for (i = 0; (i < VIRTIO_MAX_SGS) && (i < in_sgs); i++) {
9062306a36Sopenharmony_ci		__sink(in_sgs);
9162306a36Sopenharmony_ci		for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
9262306a36Sopenharmony_ci		     sgp = __sg_next(sgp)) {
9362306a36Sopenharmony_ci			bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
9462306a36Sopenharmony_ci			length2 += len;
9562306a36Sopenharmony_ci			n++;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	config = 1;
10062306a36Sopenharmony_ci	result = length2 - length1;
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
103