aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/testing/selftests/bpf/progs/syscall.c
blob: 3d3cafdebe72b06729ecef39cc710d7b88f86efe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2021 Facebook */
#include <linux/stddef.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <../../../tools/include/linux/filter.h>
#include <linux/btf.h>
#include <string.h>
#include <errno.h>

char _license[] SEC("license") = "GPL";

struct bpf_map {
	int id;
}  __attribute__((preserve_access_index));

struct args {
	__u64 log_buf;
	__u32 log_size;
	int max_entries;
	int map_fd;
	int prog_fd;
	int btf_fd;
};

#define BTF_INFO_ENC(kind, kind_flag, vlen) \
	((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
	((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
	BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
	BTF_INT_ENC(encoding, bits_offset, bits)

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__type(key, int);
	__type(value, union bpf_attr);
	__uint(max_entries, 1);
} bpf_attr_array SEC(".maps");

struct inner_map_type {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(key_size, 4);
	__uint(value_size, 4);
	__uint(max_entries, 1);
} inner_map SEC(".maps");

struct {
	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
	__type(key, int);
	__type(value, int);
	__uint(max_entries, 1);
	__array(values, struct inner_map_type);
} outer_array_map SEC(".maps") = {
	.values = {
		[0] = &inner_map,
	},
};

static inline __u64 ptr_to_u64(const void *ptr)
{
	return (__u64) (unsigned long) ptr;
}

static int btf_load(void)
{
	struct btf_blob {
		struct btf_header btf_hdr;
		__u32 types[8];
		__u32 str;
	} raw_btf = {
		.btf_hdr = {
			.magic = BTF_MAGIC,
			.version = BTF_VERSION,
			.hdr_len = sizeof(struct btf_header),
			.type_len = sizeof(__u32) * 8,
			.str_off = sizeof(__u32) * 8,
			.str_len = sizeof(__u32),
		},
		.types = {
			/* long */
			BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8),  /* [1] */
			/* unsigned long */
			BTF_TYPE_INT_ENC(0, 0, 0, 64, 8),  /* [2] */
		},
	};
	static union bpf_attr btf_load_attr = {
		.btf_size = sizeof(raw_btf),
	};

	btf_load_attr.btf = (long)&raw_btf;
	return bpf_sys_bpf(BPF_BTF_LOAD, &btf_load_attr, sizeof(btf_load_attr));
}

SEC("syscall")
int load_prog(struct args *ctx)
{
	static char license[] = "GPL";
	static struct bpf_insn insns[] = {
		BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
		BPF_LD_MAP_FD(BPF_REG_1, 0),
		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
		BPF_MOV64_IMM(BPF_REG_0, 0),
		BPF_EXIT_INSN(),
	};
	static union bpf_attr map_create_attr = {
		.map_type = BPF_MAP_TYPE_HASH,
		.key_size = 8,
		.value_size = 8,
		.btf_key_type_id = 1,
		.btf_value_type_id = 2,
	};
	static union bpf_attr map_update_attr = { .map_fd = 1, };
	static __u64 key = 12;
	static __u64 value = 34;
	static union bpf_attr prog_load_attr = {
		.prog_type = BPF_PROG_TYPE_XDP,
		.insn_cnt = sizeof(insns) / sizeof(insns[0]),
	};
	int ret;

	ret = btf_load();
	if (ret <= 0)
		return ret;

	ctx->btf_fd = ret;
	map_create_attr.max_entries = ctx->max_entries;
	map_create_attr.btf_fd = ret;

	prog_load_attr.license = ptr_to_u64(license);
	prog_load_attr.insns = ptr_to_u64(insns);
	prog_load_attr.log_buf = ctx->log_buf;
	prog_load_attr.log_size = ctx->log_size;
	prog_load_attr.log_level = 1;

	ret = bpf_sys_bpf(BPF_MAP_CREATE, &map_create_attr, sizeof(map_create_attr));
	if (ret <= 0)
		return ret;
	ctx->map_fd = ret;
	insns[3].imm = ret;

	map_update_attr.map_fd = ret;
	map_update_attr.key = ptr_to_u64(&key);
	map_update_attr.value = ptr_to_u64(&value);
	ret = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, &map_update_attr, sizeof(map_update_attr));
	if (ret < 0)
		return ret;

	ret = bpf_sys_bpf(BPF_PROG_LOAD, &prog_load_attr, sizeof(prog_load_attr));
	if (ret <= 0)
		return ret;
	ctx->prog_fd = ret;
	return 1;
}

SEC("syscall")
int update_outer_map(void *ctx)
{
	int zero = 0, ret = 0, outer_fd = -1, inner_fd = -1, err;
	const int attr_sz = sizeof(union bpf_attr);
	union bpf_attr *attr;

	attr = bpf_map_lookup_elem((struct bpf_map *)&bpf_attr_array, &zero);
	if (!attr)
		goto out;

	memset(attr, 0, attr_sz);
	attr->map_id = ((struct bpf_map *)&outer_array_map)->id;
	outer_fd = bpf_sys_bpf(BPF_MAP_GET_FD_BY_ID, attr, attr_sz);
	if (outer_fd < 0)
		goto out;

	memset(attr, 0, attr_sz);
	attr->map_type = BPF_MAP_TYPE_ARRAY;
	attr->key_size = 4;
	attr->value_size = 4;
	attr->max_entries = 1;
	inner_fd = bpf_sys_bpf(BPF_MAP_CREATE, attr, attr_sz);
	if (inner_fd < 0)
		goto out;

	memset(attr, 0, attr_sz);
	attr->map_fd = outer_fd;
	attr->key = ptr_to_u64(&zero);
	attr->value = ptr_to_u64(&inner_fd);
	err = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, attr, attr_sz);
	if (err)
		goto out;

	memset(attr, 0, attr_sz);
	attr->map_fd = outer_fd;
	attr->key = ptr_to_u64(&zero);
	err = bpf_sys_bpf(BPF_MAP_DELETE_ELEM, attr, attr_sz);
	if (err)
		goto out;
	ret = 1;
out:
	if (inner_fd >= 0)
		bpf_sys_close(inner_fd);
	if (outer_fd >= 0)
		bpf_sys_close(outer_fd);
	return ret;
}