diff options
Diffstat (limited to 'samples')
46 files changed, 1843 insertions, 371 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index a6d2a43bbf2e..b124f62ed6cb 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -105,4 +105,11 @@ config SAMPLE_BLACKFIN_GPTIMERS help Build samples of blackfin gptimers sample module. +config SAMPLE_VFIO_MDEV_MTTY + tristate "Build VFIO mtty example mediated device sample code -- loadable modules only" + depends on VFIO_MDEV_DEVICE && m + help + Build a virtual tty sample driver for use as a VFIO + mediated device + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index e17d66d77f09..86a137e451d9 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -2,4 +2,5 @@ obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ - configfs/ connector/ v4l/ trace_printk/ blackfin/ + configfs/ connector/ v4l/ trace_printk/ blackfin/ \ + vfio-mdev/ diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index f2219c1489e5..09e9d535bd74 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -35,40 +35,43 @@ hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist hostprogs-y += xdp_tx_iptunnel -test_lru_dist-objs := test_lru_dist.o libbpf.o -sock_example-objs := sock_example.o libbpf.o -fds_example-objs := bpf_load.o libbpf.o fds_example.o -sockex1-objs := bpf_load.o libbpf.o sockex1_user.o -sockex2-objs := bpf_load.o libbpf.o sockex2_user.o -sockex3-objs := bpf_load.o libbpf.o sockex3_user.o -tracex1-objs := bpf_load.o libbpf.o tracex1_user.o -tracex2-objs := bpf_load.o libbpf.o tracex2_user.o -tracex3-objs := bpf_load.o libbpf.o tracex3_user.o -tracex4-objs := bpf_load.o libbpf.o tracex4_user.o -tracex5-objs := bpf_load.o libbpf.o tracex5_user.o -tracex6-objs := bpf_load.o libbpf.o tracex6_user.o -test_probe_write_user-objs := bpf_load.o libbpf.o test_probe_write_user_user.o -trace_output-objs := bpf_load.o libbpf.o trace_output_user.o -lathist-objs := bpf_load.o libbpf.o lathist_user.o -offwaketime-objs := bpf_load.o libbpf.o offwaketime_user.o -spintest-objs := bpf_load.o libbpf.o spintest_user.o -map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o -test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o -test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o -test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o -test_cgrp2_attach2-objs := libbpf.o test_cgrp2_attach2.o cgroup_helpers.o -test_cgrp2_sock-objs := libbpf.o test_cgrp2_sock.o -test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o -xdp1-objs := bpf_load.o libbpf.o xdp1_user.o +# Libbpf dependencies +LIBBPF := ../../tools/lib/bpf/bpf.o + +test_lru_dist-objs := test_lru_dist.o $(LIBBPF) +sock_example-objs := sock_example.o $(LIBBPF) +fds_example-objs := bpf_load.o $(LIBBPF) fds_example.o +sockex1-objs := bpf_load.o $(LIBBPF) sockex1_user.o +sockex2-objs := bpf_load.o $(LIBBPF) sockex2_user.o +sockex3-objs := bpf_load.o $(LIBBPF) sockex3_user.o +tracex1-objs := bpf_load.o $(LIBBPF) tracex1_user.o +tracex2-objs := bpf_load.o $(LIBBPF) tracex2_user.o +tracex3-objs := bpf_load.o $(LIBBPF) tracex3_user.o +tracex4-objs := bpf_load.o $(LIBBPF) tracex4_user.o +tracex5-objs := bpf_load.o $(LIBBPF) tracex5_user.o +tracex6-objs := bpf_load.o $(LIBBPF) tracex6_user.o +test_probe_write_user-objs := bpf_load.o $(LIBBPF) test_probe_write_user_user.o +trace_output-objs := bpf_load.o $(LIBBPF) trace_output_user.o +lathist-objs := bpf_load.o $(LIBBPF) lathist_user.o +offwaketime-objs := bpf_load.o $(LIBBPF) offwaketime_user.o +spintest-objs := bpf_load.o $(LIBBPF) spintest_user.o +map_perf_test-objs := bpf_load.o $(LIBBPF) map_perf_test_user.o +test_overhead-objs := bpf_load.o $(LIBBPF) test_overhead_user.o +test_cgrp2_array_pin-objs := $(LIBBPF) test_cgrp2_array_pin.o +test_cgrp2_attach-objs := $(LIBBPF) test_cgrp2_attach.o +test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o cgroup_helpers.o +test_cgrp2_sock-objs := $(LIBBPF) test_cgrp2_sock.o +test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o +xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o # reuse xdp1 source intentionally -xdp2-objs := bpf_load.o libbpf.o xdp1_user.o -test_current_task_under_cgroup-objs := bpf_load.o libbpf.o cgroup_helpers.o \ +xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o +test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) cgroup_helpers.o \ test_current_task_under_cgroup_user.o -trace_event-objs := bpf_load.o libbpf.o trace_event_user.o -sampleip-objs := bpf_load.o libbpf.o sampleip_user.o -tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o -lwt_len_hist-objs := bpf_load.o libbpf.o lwt_len_hist_user.o -xdp_tx_iptunnel-objs := bpf_load.o libbpf.o xdp_tx_iptunnel_user.o +trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o +sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o +tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o +lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o +xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -104,7 +107,10 @@ always += lwt_len_hist_kern.o always += xdp_tx_iptunnel_kern.o HOSTCFLAGS += -I$(objtree)/usr/include +HOSTCFLAGS += -I$(srctree)/tools/lib/ HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ +HOSTCFLAGS += -I$(srctree)/tools/lib/ -I$(srctree)/tools/include +HOSTCFLAGS += -I$(srctree)/tools/perf HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable HOSTLOADLIBES_fds_example += -lelf @@ -141,10 +147,10 @@ CLANG ?= clang # Trick to allow make to be run from this directory all: - $(MAKE) -C ../../ $$PWD/ + $(MAKE) -C ../../ $(CURDIR)/ clean: - $(MAKE) -C ../../ M=$$PWD clean + $(MAKE) -C ../../ M=$(CURDIR) clean @rm -f *~ # Verify LLVM compiler tools are available and bpf target is supported by llc diff --git a/samples/bpf/README.rst b/samples/bpf/README.rst index a43eae3f0551..79f9a58f1872 100644 --- a/samples/bpf/README.rst +++ b/samples/bpf/README.rst @@ -1,8 +1,8 @@ eBPF sample programs ==================== -This directory contains a mini eBPF library, test stubs, verifier -test-suite and examples for using eBPF. +This directory contains a test stubs, verifier test-suite and examples +for using eBPF. The examples use libbpf from tools/lib/bpf. Build dependencies ================== diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index e30b6de94f2e..396e204888b3 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -22,25 +22,34 @@ #include <poll.h> #include <ctype.h> #include "libbpf.h" -#include "bpf_helpers.h" #include "bpf_load.h" +#include "perf-sys.h" #define DEBUGFS "/sys/kernel/debug/tracing/" static char license[128]; static int kern_version; static bool processed_sec[128]; +char bpf_log_buf[BPF_LOG_BUF_SIZE]; int map_fd[MAX_MAPS]; int prog_fd[MAX_PROGS]; int event_fd[MAX_PROGS]; int prog_cnt; int prog_array_fd = -1; +struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; +}; + static int populate_prog_array(const char *event, int prog_fd) { int ind = atoi(event), err; - err = bpf_update_elem(prog_array_fd, &ind, &prog_fd, BPF_ANY); + err = bpf_map_update_elem(prog_array_fd, &ind, &prog_fd, BPF_ANY); if (err < 0) { printf("failed to store prog_fd in prog_array\n"); return -1; @@ -58,6 +67,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) bool is_perf_event = strncmp(event, "perf_event", 10) == 0; bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0; bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0; + size_t insns_cnt = size / sizeof(struct bpf_insn); enum bpf_prog_type prog_type; char buf[256]; int fd, efd, err, id; @@ -87,9 +97,10 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) return -1; } - fd = bpf_prog_load(prog_type, prog, size, license, kern_version); + fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version, + bpf_log_buf, BPF_LOG_BUF_SIZE); if (fd < 0) { - printf("bpf_prog_load() err=%d\n%s", errno, bpf_log_buf); + printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf); return -1; } @@ -169,7 +180,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) id = atoi(buf); attr.config = id; - efd = perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); + efd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); if (efd < 0) { printf("event %d fd %d err %s\n", id, efd, strerror(errno)); return -1; diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index fb46a421ab41..c827827299b3 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -1,12 +1,15 @@ #ifndef __BPF_LOAD_H #define __BPF_LOAD_H +#include "libbpf.h" + #define MAX_MAPS 32 #define MAX_PROGS 32 extern int map_fd[MAX_MAPS]; extern int prog_fd[MAX_PROGS]; extern int event_fd[MAX_PROGS]; +extern char bpf_log_buf[BPF_LOG_BUF_SIZE]; extern int prog_cnt; /* parses elf file compiled by llvm .c->.o diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c index 625e797be6ef..e29bd52ff9e8 100644 --- a/samples/bpf/fds_example.c +++ b/samples/bpf/fds_example.c @@ -14,6 +14,7 @@ #include "bpf_load.h" #include "libbpf.h" +#include "sock_example.h" #define BPF_F_PIN (1 << 0) #define BPF_F_GET (1 << 1) @@ -49,17 +50,19 @@ static int bpf_map_create(void) static int bpf_prog_create(const char *object) { - static const struct bpf_insn insns[] = { + static struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 1), BPF_EXIT_INSN(), }; + size_t insns_cnt = sizeof(insns) / sizeof(struct bpf_insn); if (object) { assert(!load_bpf_file((char *)object)); return prog_fd[0]; } else { - return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, - insns, sizeof(insns), "GPL", 0); + return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, + insns, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); } } @@ -83,12 +86,12 @@ static int bpf_do_map(const char *file, uint32_t flags, uint32_t key, } if ((flags & BPF_F_KEY_VAL) == BPF_F_KEY_VAL) { - ret = bpf_update_elem(fd, &key, &value, 0); + ret = bpf_map_update_elem(fd, &key, &value, 0); printf("bpf: fd:%d u->(%u:%u) ret:(%d,%s)\n", fd, key, value, ret, strerror(errno)); assert(ret == 0); } else if (flags & BPF_F_KEY) { - ret = bpf_lookup_elem(fd, &key, &value); + ret = bpf_map_lookup_elem(fd, &key, &value); printf("bpf: fd:%d l->(%u):%u ret:(%d,%s)\n", fd, key, value, ret, strerror(errno)); assert(ret == 0); diff --git a/samples/bpf/lathist_user.c b/samples/bpf/lathist_user.c index 65da8c1576de..6477bad5b4e2 100644 --- a/samples/bpf/lathist_user.c +++ b/samples/bpf/lathist_user.c @@ -73,7 +73,7 @@ static void get_data(int fd) for (c = 0; c < MAX_CPU; c++) { for (i = 0; i < MAX_ENTRIES; i++) { key = c * MAX_ENTRIES + i; - bpf_lookup_elem(fd, &key, &value); + bpf_map_lookup_elem(fd, &key, &value); cpu_hist[c].data[i] = value; if (value > cpu_hist[c].max) diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c deleted file mode 100644 index 9ce707bf02a7..000000000000 --- a/samples/bpf/libbpf.c +++ /dev/null @@ -1,176 +0,0 @@ -/* eBPF mini library */ -#include <stdlib.h> -#include <stdio.h> -#include <linux/unistd.h> -#include <unistd.h> -#include <string.h> -#include <linux/netlink.h> -#include <linux/bpf.h> -#include <errno.h> -#include <net/ethernet.h> -#include <net/if.h> -#include <linux/if_packet.h> -#include <arpa/inet.h> -#include "libbpf.h" - -static __u64 ptr_to_u64(void *ptr) -{ - return (__u64) (unsigned long) ptr; -} - -int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, - int max_entries, int map_flags) -{ - union bpf_attr attr = { - .map_type = map_type, - .key_size = key_size, - .value_size = value_size, - .max_entries = max_entries, - .map_flags = map_flags, - }; - - return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); -} - -int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags) -{ - union bpf_attr attr = { - .map_fd = fd, - .key = ptr_to_u64(key), - .value = ptr_to_u64(value), - .flags = flags, - }; - - return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); -} - -int bpf_lookup_elem(int fd, void *key, void *value) -{ - union bpf_attr attr = { - .map_fd = fd, - .key = ptr_to_u64(key), - .value = ptr_to_u64(value), - }; - - return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); -} - -int bpf_delete_elem(int fd, void *key) -{ - union bpf_attr attr = { - .map_fd = fd, - .key = ptr_to_u64(key), - }; - - return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); -} - -int bpf_get_next_key(int fd, void *key, void *next_key) -{ - union bpf_attr attr = { - .map_fd = fd, - .key = ptr_to_u64(key), - .next_key = ptr_to_u64(next_key), - }; - - return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); -} - -#define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u)) - -char bpf_log_buf[LOG_BUF_SIZE]; - -int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int prog_len, - const char *license, int kern_version) -{ - union bpf_attr attr = { - .prog_type = prog_type, - .insns = ptr_to_u64((void *) insns), - .insn_cnt = prog_len / sizeof(struct bpf_insn), - .license = ptr_to_u64((void *) license), - .log_buf = ptr_to_u64(bpf_log_buf), - .log_size = LOG_BUF_SIZE, - .log_level = 1, - }; - - /* assign one field outside of struct init to make sure any - * padding is zero initialized - */ - attr.kern_version = kern_version; - - bpf_log_buf[0] = 0; - - return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); -} - -int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type) -{ - union bpf_attr attr = { - .target_fd = target_fd, - .attach_bpf_fd = prog_fd, - .attach_type = type, - }; - - return syscall(__NR_bpf, BPF_PROG_ATTACH, &attr, sizeof(attr)); -} - -int bpf_prog_detach(int target_fd, enum bpf_attach_type type) -{ - union bpf_attr attr = { - .target_fd = target_fd, - .attach_type = type, - }; - - return syscall(__NR_bpf, BPF_PROG_DETACH, &attr, sizeof(attr)); -} - -int bpf_obj_pin(int fd, const char *pathname) -{ - union bpf_attr attr = { - .pathname = ptr_to_u64((void *)pathname), - .bpf_fd = fd, - }; - - return syscall(__NR_bpf, BPF_OBJ_PIN, &attr, sizeof(attr)); -} - -int bpf_obj_get(const char *pathname) -{ - union bpf_attr attr = { - .pathname = ptr_to_u64((void *)pathname), - }; - - return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); -} - -int open_raw_sock(const char *name) -{ - struct sockaddr_ll sll; - int sock; - - sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); - if (sock < 0) { - printf("cannot create raw socket\n"); - return -1; - } - - memset(&sll, 0, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_ifindex = if_nametoindex(name); - sll.sll_protocol = htons(ETH_P_ALL); - if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { - printf("bind to %s: %s\n", name, strerror(errno)); - close(sock); - return -1; - } - - return sock; -} - -int perf_event_open(struct perf_event_attr *attr, int pid, int cpu, - int group_fd, unsigned long flags) -{ - return syscall(__NR_perf_event_open, attr, pid, cpu, - group_fd, flags); -} diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index 94a901d86fc2..3705fba453a0 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -2,27 +2,9 @@ #ifndef __LIBBPF_H #define __LIBBPF_H -struct bpf_insn; - -int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, - int max_entries, int map_flags); -int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); -int bpf_lookup_elem(int fd, void *key, void *value); -int bpf_delete_elem(int fd, void *key); -int bpf_get_next_key(int fd, void *key, void *next_key); - -int bpf_prog_load(enum bpf_prog_type prog_type, - const struct bpf_insn *insns, int insn_len, - const char *license, int kern_version); - -int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type); -int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); +#include <bpf/bpf.h> -int bpf_obj_pin(int fd, const char *pathname); -int bpf_obj_get(const char *pathname); - -#define LOG_BUF_SIZE (256 * 1024) -extern char bpf_log_buf[LOG_BUF_SIZE]; +struct bpf_insn; /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ @@ -203,10 +185,4 @@ extern char bpf_log_buf[LOG_BUF_SIZE]; .off = 0, \ .imm = 0 }) -/* create RAW socket and bind to interface 'name' */ -int open_raw_sock(const char *name); - -struct perf_event_attr; -int perf_event_open(struct perf_event_attr *attr, int pid, int cpu, - int group_fd, unsigned long flags); #endif diff --git a/samples/bpf/lwt_len_hist_user.c b/samples/bpf/lwt_len_hist_user.c index 05d783fc5daf..ec8f3bbcbef3 100644 --- a/samples/bpf/lwt_len_hist_user.c +++ b/samples/bpf/lwt_len_hist_user.c @@ -14,6 +14,8 @@ #define MAX_INDEX 64 #define MAX_STARS 38 +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + static void stars(char *str, long val, long max, int width) { int i; @@ -41,13 +43,13 @@ int main(int argc, char **argv) return -1; } - while (bpf_get_next_key(map_fd, &key, &next_key) == 0) { + while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) { if (next_key >= MAX_INDEX) { fprintf(stderr, "Key %lu out of bounds\n", next_key); continue; } - bpf_lookup_elem(map_fd, &next_key, values); + bpf_map_lookup_elem(map_fd, &next_key, values); sum = 0; for (i = 0; i < nr_cpus; i++) diff --git a/samples/bpf/offwaketime_user.c b/samples/bpf/offwaketime_user.c index 6f002a9c24fa..9cce2a66bd66 100644 --- a/samples/bpf/offwaketime_user.c +++ b/samples/bpf/offwaketime_user.c @@ -49,14 +49,14 @@ static void print_stack(struct key_t *key, __u64 count) int i; printf("%s;", key->target); - if (bpf_lookup_elem(map_fd[3], &key->tret, ip) != 0) { + if (bpf_map_lookup_elem(map_fd[3], &key->tret, ip) != 0) { printf("---;"); } else { for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) print_ksym(ip[i]); } printf("-;"); - if (bpf_lookup_elem(map_fd[3], &key->wret, ip) != 0) { + if (bpf_map_lookup_elem(map_fd[3], &key->wret, ip) != 0) { printf("---;"); } else { for (i = 0; i < PERF_MAX_STACK_DEPTH; i++) @@ -77,8 +77,8 @@ static void print_stacks(int fd) struct key_t key = {}, next_key; __u64 value; - while (bpf_get_next_key(fd, &key, &next_key) == 0) { - bpf_lookup_elem(fd, &next_key, &value); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + bpf_map_lookup_elem(fd, &next_key, &value); print_stack(&next_key, value); key = next_key; } diff --git a/samples/bpf/sampleip_user.c b/samples/bpf/sampleip_user.c index 260a6bdd6413..be59d7dcbdde 100644 --- a/samples/bpf/sampleip_user.c +++ b/samples/bpf/sampleip_user.c @@ -21,6 +21,7 @@ #include <sys/ioctl.h> #include "libbpf.h" #include "bpf_load.h" +#include "perf-sys.h" #define DEFAULT_FREQ 99 #define DEFAULT_SECS 5 @@ -49,7 +50,7 @@ static int sampling_start(int *pmu_fd, int freq) }; for (i = 0; i < nr_cpus; i++) { - pmu_fd[i] = perf_event_open(&pe_sample_attr, -1 /* pid */, i, + pmu_fd[i] = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i, -1 /* group_fd */, 0 /* flags */); if (pmu_fd[i] < 0) { fprintf(stderr, "ERROR: Initializing perf sampling\n"); @@ -95,8 +96,8 @@ static void print_ip_map(int fd) /* fetch IPs and counts */ key = 0, i = 0; - while (bpf_get_next_key(fd, &key, &next_key) == 0) { - bpf_lookup_elem(fd, &next_key, &value); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + bpf_map_lookup_elem(fd, &next_key, &value); counts[i].ip = next_key; counts[i++].count = value; key = next_key; diff --git a/samples/bpf/sock_example.c b/samples/bpf/sock_example.c index 28b60baa9fa8..6fc6e193ef1b 100644 --- a/samples/bpf/sock_example.c +++ b/samples/bpf/sock_example.c @@ -27,6 +27,9 @@ #include <linux/ip.h> #include <stddef.h> #include "libbpf.h" +#include "sock_example.h" + +char bpf_log_buf[BPF_LOG_BUF_SIZE]; static int test_sock(void) { @@ -54,9 +57,10 @@ static int test_sock(void) BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */ BPF_EXIT_INSN(), }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), - "GPL", 0); + prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt, + "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE); if (prog_fd < 0) { printf("failed to load prog '%s'\n", strerror(errno)); goto cleanup; @@ -72,13 +76,13 @@ static int test_sock(void) for (i = 0; i < 10; i++) { key = IPPROTO_TCP; - assert(bpf_lookup_elem(map_fd, &key, &tcp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd, &key, &tcp_cnt) == 0); key = IPPROTO_UDP; - assert(bpf_lookup_elem(map_fd, &key, &udp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd, &key, &udp_cnt) == 0); key = IPPROTO_ICMP; - assert(bpf_lookup_elem(map_fd, &key, &icmp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd, &key, &icmp_cnt) == 0); printf("TCP %lld UDP %lld ICMP %lld packets\n", tcp_cnt, udp_cnt, icmp_cnt); diff --git a/samples/bpf/sock_example.h b/samples/bpf/sock_example.h new file mode 100644 index 000000000000..d8014065d479 --- /dev/null +++ b/samples/bpf/sock_example.h @@ -0,0 +1,35 @@ +#include <stdlib.h> +#include <stdio.h> +#include <linux/unistd.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <linux/if_ether.h> +#include <net/if.h> +#include <linux/if_packet.h> +#include <arpa/inet.h> +#include "libbpf.h" + +static inline int open_raw_sock(const char *name) +{ + struct sockaddr_ll sll; + int sock; + + sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); + if (sock < 0) { + printf("cannot create raw socket\n"); + return -1; + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = if_nametoindex(name); + sll.sll_protocol = htons(ETH_P_ALL); + if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { + printf("bind to %s: %s\n", name, strerror(errno)); + close(sock); + return -1; + } + + return sock; +} diff --git a/samples/bpf/sockex1_user.c b/samples/bpf/sockex1_user.c index 678ce4693551..6cd2feb3e9b3 100644 --- a/samples/bpf/sockex1_user.c +++ b/samples/bpf/sockex1_user.c @@ -3,6 +3,7 @@ #include <linux/bpf.h> #include "libbpf.h" #include "bpf_load.h" +#include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> @@ -32,13 +33,13 @@ int main(int ac, char **argv) int key; key = IPPROTO_TCP; - assert(bpf_lookup_elem(map_fd[0], &key, &tcp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd[0], &key, &tcp_cnt) == 0); key = IPPROTO_UDP; - assert(bpf_lookup_elem(map_fd[0], &key, &udp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd[0], &key, &udp_cnt) == 0); key = IPPROTO_ICMP; - assert(bpf_lookup_elem(map_fd[0], &key, &icmp_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd[0], &key, &icmp_cnt) == 0); printf("TCP %lld UDP %lld ICMP %lld bytes\n", tcp_cnt, udp_cnt, icmp_cnt); diff --git a/samples/bpf/sockex2_user.c b/samples/bpf/sockex2_user.c index 8a4085c2d117..0e0207c90841 100644 --- a/samples/bpf/sockex2_user.c +++ b/samples/bpf/sockex2_user.c @@ -3,6 +3,7 @@ #include <linux/bpf.h> #include "libbpf.h" #include "bpf_load.h" +#include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> #include <sys/resource.h> @@ -39,8 +40,8 @@ int main(int ac, char **argv) int key = 0, next_key; struct pair value; - while (bpf_get_next_key(map_fd[0], &key, &next_key) == 0) { - bpf_lookup_elem(map_fd[0], &next_key, &value); + while (bpf_map_get_next_key(map_fd[0], &key, &next_key) == 0) { + bpf_map_lookup_elem(map_fd[0], &next_key, &value); printf("ip %s bytes %lld packets %lld\n", inet_ntoa((struct in_addr){htonl(next_key)}), value.bytes, value.packets); diff --git a/samples/bpf/sockex3_user.c b/samples/bpf/sockex3_user.c index 3fcfd8c4b2a3..b5524d417eb5 100644 --- a/samples/bpf/sockex3_user.c +++ b/samples/bpf/sockex3_user.c @@ -3,6 +3,7 @@ #include <linux/bpf.h> #include "libbpf.h" #include "bpf_load.h" +#include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> #include <sys/resource.h> @@ -54,8 +55,8 @@ int main(int argc, char **argv) sleep(1); printf("IP src.port -> dst.port bytes packets\n"); - while (bpf_get_next_key(map_fd[2], &key, &next_key) == 0) { - bpf_lookup_elem(map_fd[2], &next_key, &value); + while (bpf_map_get_next_key(map_fd[2], &key, &next_key) == 0) { + bpf_map_lookup_elem(map_fd[2], &next_key, &value); printf("%s.%05d -> %s.%05d %12lld %12lld\n", inet_ntoa((struct in_addr){htonl(next_key.src)}), next_key.port16[0], diff --git a/samples/bpf/spintest_user.c b/samples/bpf/spintest_user.c index 311ede532230..80676c25fa50 100644 --- a/samples/bpf/spintest_user.c +++ b/samples/bpf/spintest_user.c @@ -31,8 +31,8 @@ int main(int ac, char **argv) for (i = 0; i < 5; i++) { key = 0; printf("kprobing funcs:"); - while (bpf_get_next_key(map_fd[0], &key, &next_key) == 0) { - bpf_lookup_elem(map_fd[0], &next_key, &value); + while (bpf_map_get_next_key(map_fd[0], &key, &next_key) == 0) { + bpf_map_lookup_elem(map_fd[0], &next_key, &value); assert(next_key == value); sym = ksym_search(value); printf(" %s", sym->name); @@ -41,8 +41,8 @@ int main(int ac, char **argv) if (key) printf("\n"); key = 0; - while (bpf_get_next_key(map_fd[0], &key, &next_key) == 0) - bpf_delete_elem(map_fd[0], &next_key); + while (bpf_map_get_next_key(map_fd[0], &key, &next_key) == 0) + bpf_map_delete_elem(map_fd[0], &next_key); sleep(1); } diff --git a/samples/bpf/tc_l2_redirect_kern.c b/samples/bpf/tc_l2_redirect_kern.c index 92a44729dbe4..7ef2a12b25b2 100644 --- a/samples/bpf/tc_l2_redirect_kern.c +++ b/samples/bpf/tc_l2_redirect_kern.c @@ -4,6 +4,7 @@ * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ +#define KBUILD_MODNAME "foo" #include <uapi/linux/bpf.h> #include <uapi/linux/if_ether.h> #include <uapi/linux/if_packet.h> diff --git a/samples/bpf/tc_l2_redirect_user.c b/samples/bpf/tc_l2_redirect_user.c index 4013c5337b91..28995a776560 100644 --- a/samples/bpf/tc_l2_redirect_user.c +++ b/samples/bpf/tc_l2_redirect_user.c @@ -60,9 +60,9 @@ int main(int argc, char **argv) } /* bpf_tunnel_key.remote_ipv4 expects host byte orders */ - ret = bpf_update_elem(array_fd, &array_key, &ifindex, 0); + ret = bpf_map_update_elem(array_fd, &array_key, &ifindex, 0); if (ret) { - perror("bpf_update_elem"); + perror("bpf_map_update_elem"); goto out; } diff --git a/samples/bpf/test_cgrp2_array_pin.c b/samples/bpf/test_cgrp2_array_pin.c index 70e86f7be69d..8a1b8b5d8def 100644 --- a/samples/bpf/test_cgrp2_array_pin.c +++ b/samples/bpf/test_cgrp2_array_pin.c @@ -85,9 +85,9 @@ int main(int argc, char **argv) } } - ret = bpf_update_elem(array_fd, &array_key, &cg2_fd, 0); + ret = bpf_map_update_elem(array_fd, &array_key, &cg2_fd, 0); if (ret) { - perror("bpf_update_elem"); + perror("bpf_map_update_elem"); goto out; } diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c index a19484c45b79..4bfcaf93fcf3 100644 --- a/samples/bpf/test_cgrp2_attach.c +++ b/samples/bpf/test_cgrp2_attach.c @@ -36,6 +36,8 @@ enum { MAP_KEY_BYTES, }; +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + static int prog_load(int map_fd, int verdict) { struct bpf_insn prog[] = { @@ -66,9 +68,11 @@ static int prog_load(int map_fd, int verdict) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ BPF_EXIT_INSN(), }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, - prog, sizeof(prog), "GPL", 0); + return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); } static int usage(const char *argv0) @@ -100,7 +104,7 @@ static int attach_filter(int cg_fd, int type, int verdict) return EXIT_FAILURE; } - ret = bpf_prog_attach(prog_fd, cg_fd, type); + ret = bpf_prog_attach(prog_fd, cg_fd, type, 0); if (ret < 0) { printf("Failed to attach prog to cgroup: '%s'\n", strerror(errno)); @@ -108,10 +112,10 @@ static int attach_filter(int cg_fd, int type, int verdict) } while (1) { key = MAP_KEY_PACKETS; - assert(bpf_lookup_elem(map_fd, &key, &pkt_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd, &key, &pkt_cnt) == 0); key = MAP_KEY_BYTES; - assert(bpf_lookup_elem(map_fd, &key, &byte_cnt) == 0); + assert(bpf_map_lookup_elem(map_fd, &key, &byte_cnt) == 0); printf("cgroup received %lld packets, %lld bytes\n", pkt_cnt, byte_cnt); diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c index ddfac42ed4df..3049b1f26267 100644 --- a/samples/bpf/test_cgrp2_attach2.c +++ b/samples/bpf/test_cgrp2_attach2.c @@ -32,6 +32,8 @@ #define BAR "/foo/bar/" #define PING_CMD "ping -c1 -w1 127.0.0.1" +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + static int prog_load(int verdict) { int ret; @@ -39,9 +41,11 @@ static int prog_load(int verdict) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ BPF_EXIT_INSN(), }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, - prog, sizeof(prog), "GPL", 0); + ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); if (ret < 0) { log_err("Loading program"); @@ -75,11 +79,12 @@ int main(int argc, char **argv) if (join_cgroup(FOO)) goto err; - if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS)) { + if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 1)) { log_err("Attaching prog to /foo"); goto err; } + printf("Attached DROP prog. This ping in cgroup /foo should fail...\n"); assert(system(PING_CMD) != 0); /* Create cgroup /foo/bar, get fd, and join it */ @@ -90,24 +95,27 @@ int main(int argc, char **argv) if (join_cgroup(BAR)) goto err; + printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n"); assert(system(PING_CMD) != 0); - if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) { + if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) { log_err("Attaching prog to /foo/bar"); goto err; } + printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n"); assert(system(PING_CMD) == 0); - if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { log_err("Detaching program from /foo/bar"); goto err; } + printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n" + "This ping in cgroup /foo/bar should fail...\n"); assert(system(PING_CMD) != 0); - if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS)) { + if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) { log_err("Attaching prog to /foo/bar"); goto err; } @@ -117,8 +125,60 @@ int main(int argc, char **argv) goto err; } + printf("Attached PASS from /foo/bar and detached DROP from /foo.\n" + "This ping in cgroup /foo/bar should pass...\n"); assert(system(PING_CMD) == 0); + if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) { + log_err("Attaching prog to /foo/bar"); + goto err; + } + + if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { + errno = 0; + log_err("Unexpected success attaching prog to /foo/bar"); + goto err; + } + + if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { + log_err("Detaching program from /foo/bar"); + goto err; + } + + if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { + errno = 0; + log_err("Unexpected success in double detach from /foo"); + goto err; + } + + if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { + log_err("Attaching non-overridable prog to /foo"); + goto err; + } + + if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { + errno = 0; + log_err("Unexpected success attaching non-overridable prog to /foo/bar"); + goto err; + } + + if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) { + errno = 0; + log_err("Unexpected success attaching overridable prog to /foo/bar"); + goto err; + } + + if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 1)) { + errno = 0; + log_err("Unexpected success attaching overridable prog to /foo"); + goto err; + } + + if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { + log_err("Attaching different non-overridable prog to /foo"); + goto err; + } + goto out; err: @@ -128,5 +188,9 @@ out: close(foo); close(bar); cleanup_cgroup_environment(); + if (!rc) + printf("PASS\n"); + else + printf("FAIL\n"); return rc; } diff --git a/samples/bpf/test_cgrp2_sock.c b/samples/bpf/test_cgrp2_sock.c index d467b3c1c55c..c3cfb23e23b5 100644 --- a/samples/bpf/test_cgrp2_sock.c +++ b/samples/bpf/test_cgrp2_sock.c @@ -23,6 +23,8 @@ #include "libbpf.h" +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + static int prog_load(int idx) { struct bpf_insn prog[] = { @@ -33,9 +35,10 @@ static int prog_load(int idx) BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ BPF_EXIT_INSN(), }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog), - "GPL", 0); + return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt, + "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE); } static int usage(const char *argv0) @@ -72,7 +75,7 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE); + ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0); if (ret < 0) { printf("Failed to attach prog to cgroup: '%s'\n", strerror(errno)); diff --git a/samples/bpf/test_cgrp2_sock2.c b/samples/bpf/test_cgrp2_sock2.c index 455ef0d06e93..db036077b644 100644 --- a/samples/bpf/test_cgrp2_sock2.c +++ b/samples/bpf/test_cgrp2_sock2.c @@ -55,7 +55,7 @@ int main(int argc, char **argv) } ret = bpf_prog_attach(prog_fd[filter_id], cg_fd, - BPF_CGROUP_INET_SOCK_CREATE); + BPF_CGROUP_INET_SOCK_CREATE, 0); if (ret < 0) { printf("Failed to attach prog to cgroup: '%s'\n", strerror(errno)); diff --git a/samples/bpf/test_current_task_under_cgroup_user.c b/samples/bpf/test_current_task_under_cgroup_user.c index 95aaaa846130..65b5fb51c1db 100644 --- a/samples/bpf/test_current_task_under_cgroup_user.c +++ b/samples/bpf/test_current_task_under_cgroup_user.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) if (!cg2) goto err; - if (bpf_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { + if (bpf_map_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { log_err("Adding target cgroup to map"); goto err; } @@ -50,7 +50,7 @@ int main(int argc, char **argv) */ sync(); - bpf_lookup_elem(map_fd[1], &idx, &remote_pid); + bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid); if (local_pid != remote_pid) { fprintf(stderr, @@ -64,10 +64,10 @@ int main(int argc, char **argv) goto err; remote_pid = 0; - bpf_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); + bpf_map_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); sync(); - bpf_lookup_elem(map_fd[1], &idx, &remote_pid); + bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid); if (local_pid == remote_pid) { fprintf(stderr, "BPF cgroup negative test did not work\n"); diff --git a/samples/bpf/test_lru_dist.c b/samples/bpf/test_lru_dist.c index 316230a0ed23..d96dc88d3b04 100644 --- a/samples/bpf/test_lru_dist.c +++ b/samples/bpf/test_lru_dist.c @@ -134,7 +134,7 @@ static int pfect_lru_lookup_or_insert(struct pfect_lru *lru, int seen = 0; lru->total++; - if (!bpf_lookup_elem(lru->map_fd, &key, &node)) { + if (!bpf_map_lookup_elem(lru->map_fd, &key, &node)) { if (node) { list_move(&node->list, &lru->list); return 1; @@ -151,7 +151,7 @@ static int pfect_lru_lookup_or_insert(struct pfect_lru *lru, node = list_last_entry(&lru->list, struct pfect_lru_node, list); - bpf_update_elem(lru->map_fd, &node->key, &null_node, BPF_EXIST); + bpf_map_update_elem(lru->map_fd, &node->key, &null_node, BPF_EXIST); } node->key = key; @@ -159,10 +159,10 @@ static int pfect_lru_lookup_or_insert(struct pfect_lru *lru, lru->nr_misses++; if (seen) { - assert(!bpf_update_elem(lru->map_fd, &key, &node, BPF_EXIST)); + assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_EXIST)); } else { lru->nr_unique++; - assert(!bpf_update_elem(lru->map_fd, &key, &node, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_NOEXIST)); } return seen; @@ -285,11 +285,11 @@ static void do_test_lru_dist(int task, void *data) pfect_lru_lookup_or_insert(&pfect_lru, key); - if (!bpf_lookup_elem(lru_map_fd, &key, &value)) + if (!bpf_map_lookup_elem(lru_map_fd, &key, &value)) continue; - if (bpf_update_elem(lru_map_fd, &key, &value, BPF_NOEXIST)) { - printf("bpf_update_elem(lru_map_fd, %llu): errno:%d\n", + if (bpf_map_update_elem(lru_map_fd, &key, &value, BPF_NOEXIST)) { + printf("bpf_map_update_elem(lru_map_fd, %llu): errno:%d\n", key, errno); assert(0); } @@ -358,19 +358,19 @@ static void test_lru_loss0(int map_type, int map_flags) for (key = 1; key <= 1000; key++) { int start_key, end_key; - assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0); + assert(bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0); start_key = 101; end_key = min(key, 900); while (start_key <= end_key) { - bpf_lookup_elem(map_fd, &start_key, value); + bpf_map_lookup_elem(map_fd, &start_key, value); start_key++; } } for (key = 1; key <= 1000; key++) { - if (bpf_lookup_elem(map_fd, &key, value)) { + if (bpf_map_lookup_elem(map_fd, &key, value)) { if (key <= 100) old_unused_losses++; else if (key <= 900) @@ -408,10 +408,10 @@ static void test_lru_loss1(int map_type, int map_flags) value[0] = 1234; for (key = 1; key <= 1000; key++) - assert(!bpf_update_elem(map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST)); for (key = 1; key <= 1000; key++) { - if (bpf_lookup_elem(map_fd, &key, value)) + if (bpf_map_lookup_elem(map_fd, &key, value)) nr_losses++; } @@ -436,7 +436,7 @@ static void do_test_parallel_lru_loss(int task, void *data) next_ins_key = stable_base; value[0] = 1234; for (i = 0; i < nr_stable_elems; i++) { - assert(bpf_update_elem(map_fd, &next_ins_key, value, + assert(bpf_map_update_elem(map_fd, &next_ins_key, value, BPF_NOEXIST) == 0); next_ins_key++; } @@ -448,9 +448,9 @@ static void do_test_parallel_lru_loss(int task, void *data) if (rn % 10) { key = rn % nr_stable_elems + stable_base; - bpf_lookup_elem(map_fd, &key, value); + bpf_map_lookup_elem(map_fd, &key, value); } else { - bpf_update_elem(map_fd, &next_ins_key, value, + bpf_map_update_elem(map_fd, &next_ins_key, value, BPF_NOEXIST); next_ins_key++; } @@ -458,7 +458,7 @@ static void do_test_parallel_lru_loss(int task, void *data) key = stable_base; for (i = 0; i < nr_stable_elems; i++) { - if (bpf_lookup_elem(map_fd, &key, value)) + if (bpf_map_lookup_elem(map_fd, &key, value)) nr_losses++; key++; } diff --git a/samples/bpf/test_probe_write_user_user.c b/samples/bpf/test_probe_write_user_user.c index a44bf347bedd..b5bf178a6ecc 100644 --- a/samples/bpf/test_probe_write_user_user.c +++ b/samples/bpf/test_probe_write_user_user.c @@ -50,7 +50,7 @@ int main(int ac, char **argv) mapped_addr_in->sin_port = htons(5555); mapped_addr_in->sin_addr.s_addr = inet_addr("255.255.255.255"); - assert(!bpf_update_elem(map_fd[0], &mapped_addr, &serv_addr, BPF_ANY)); + assert(!bpf_map_update_elem(map_fd[0], &mapped_addr, &serv_addr, BPF_ANY)); assert(listen(serverfd, 5) == 0); diff --git a/samples/bpf/trace_event_user.c b/samples/bpf/trace_event_user.c index 9a130d31ecf2..0c5561d193a4 100644 --- a/samples/bpf/trace_event_user.c +++ b/samples/bpf/trace_event_user.c @@ -20,6 +20,7 @@ #include <sys/resource.h> #include "libbpf.h" #include "bpf_load.h" +#include "perf-sys.h" #define SAMPLE_FREQ 50 @@ -61,14 +62,14 @@ static void print_stack(struct key_t *key, __u64 count) int i; printf("%3lld %s;", count, key->comm); - if (bpf_lookup_elem(map_fd[1], &key->kernstack, ip) != 0) { + if (bpf_map_lookup_elem(map_fd[1], &key->kernstack, ip) != 0) { printf("---;"); } else { for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) print_ksym(ip[i]); } printf("-;"); - if (bpf_lookup_elem(map_fd[1], &key->userstack, ip) != 0) { + if (bpf_map_lookup_elem(map_fd[1], &key->userstack, ip) != 0) { printf("---;"); } else { for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) @@ -98,10 +99,10 @@ static void print_stacks(void) int fd = map_fd[0], stack_map = map_fd[1]; sys_read_seen = sys_write_seen = false; - while (bpf_get_next_key(fd, &key, &next_key) == 0) { - bpf_lookup_elem(fd, &next_key, &value); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + bpf_map_lookup_elem(fd, &next_key, &value); print_stack(&next_key, value); - bpf_delete_elem(fd, &next_key); + bpf_map_delete_elem(fd, &next_key); key = next_key; } @@ -111,8 +112,8 @@ static void print_stacks(void) } /* clear stack map */ - while (bpf_get_next_key(stack_map, &stackid, &next_id) == 0) { - bpf_delete_elem(stack_map, &next_id); + while (bpf_map_get_next_key(stack_map, &stackid, &next_id) == 0) { + bpf_map_delete_elem(stack_map, &next_id); stackid = next_id; } } @@ -125,9 +126,9 @@ static void test_perf_event_all_cpu(struct perf_event_attr *attr) /* open perf_event on all cpus */ for (i = 0; i < nr_cpus; i++) { - pmu_fd[i] = perf_event_open(attr, -1, i, -1, 0); + pmu_fd[i] = sys_perf_event_open(attr, -1, i, -1, 0); if (pmu_fd[i] < 0) { - printf("perf_event_open failed\n"); + printf("sys_perf_event_open failed\n"); goto all_cpu_err; } assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); @@ -146,9 +147,9 @@ static void test_perf_event_task(struct perf_event_attr *attr) int pmu_fd; /* open task bound event */ - pmu_fd = perf_event_open(attr, 0, -1, -1, 0); + pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0); if (pmu_fd < 0) { - printf("perf_event_open failed\n"); + printf("sys_perf_event_open failed\n"); return; } assert(ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); diff --git a/samples/bpf/trace_output_user.c b/samples/bpf/trace_output_user.c index 661a7d052f2c..ccca1e348017 100644 --- a/samples/bpf/trace_output_user.c +++ b/samples/bpf/trace_output_user.c @@ -9,7 +9,6 @@ #include <string.h> #include <fcntl.h> #include <poll.h> -#include <sys/ioctl.h> #include <linux/perf_event.h> #include <linux/bpf.h> #include <errno.h> @@ -21,6 +20,7 @@ #include <signal.h> #include "libbpf.h" #include "bpf_load.h" +#include "perf-sys.h" static int pmu_fd; @@ -61,7 +61,7 @@ struct perf_event_sample { char data[]; }; -void perf_event_read(print_fn fn) +static void perf_event_read(print_fn fn) { __u64 data_tail = header->data_tail; __u64 data_head = header->data_head; @@ -159,10 +159,10 @@ static void test_bpf_perf_event(void) }; int key = 0; - pmu_fd = perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); + pmu_fd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/, 0); assert(pmu_fd >= 0); - assert(bpf_update_elem(map_fd[0], &key, &pmu_fd, BPF_ANY) == 0); + assert(bpf_map_update_elem(map_fd[0], &key, &pmu_fd, BPF_ANY) == 0); ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); } diff --git a/samples/bpf/tracex2_user.c b/samples/bpf/tracex2_user.c index 3e225e331f66..ded9804c5034 100644 --- a/samples/bpf/tracex2_user.c +++ b/samples/bpf/tracex2_user.c @@ -48,12 +48,12 @@ static void print_hist_for_pid(int fd, void *task) long max_value = 0; int i, ind; - while (bpf_get_next_key(fd, &key, &next_key) == 0) { + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { if (memcmp(&next_key, task, SIZE)) { key = next_key; continue; } - bpf_lookup_elem(fd, &next_key, values); + bpf_map_lookup_elem(fd, &next_key, values); value = 0; for (i = 0; i < nr_cpus; i++) value += values[i]; @@ -83,7 +83,7 @@ static void print_hist(int fd) int task_cnt = 0; int i; - while (bpf_get_next_key(fd, &key, &next_key) == 0) { + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { int found = 0; for (i = 0; i < task_cnt; i++) @@ -136,8 +136,8 @@ int main(int ac, char **argv) for (i = 0; i < 5; i++) { key = 0; - while (bpf_get_next_key(map_fd[0], &key, &next_key) == 0) { - bpf_lookup_elem(map_fd[0], &next_key, &value); + while (bpf_map_get_next_key(map_fd[0], &key, &next_key) == 0) { + bpf_map_lookup_elem(map_fd[0], &next_key, &value); printf("location 0x%lx count %ld\n", next_key, value); key = next_key; } diff --git a/samples/bpf/tracex3_user.c b/samples/bpf/tracex3_user.c index d0851cb4fa8d..8f7d199d5945 100644 --- a/samples/bpf/tracex3_user.c +++ b/samples/bpf/tracex3_user.c @@ -28,7 +28,7 @@ static void clear_stats(int fd) memset(values, 0, sizeof(values)); for (key = 0; key < SLOTS; key++) - bpf_update_elem(fd, &key, values, BPF_ANY); + bpf_map_update_elem(fd, &key, values, BPF_ANY); } const char *color[] = { @@ -89,7 +89,7 @@ static void print_hist(int fd) int i; for (key = 0; key < SLOTS; key++) { - bpf_lookup_elem(fd, &key, values); + bpf_map_lookup_elem(fd, &key, values); value = 0; for (i = 0; i < nr_cpus; i++) value += values[i]; diff --git a/samples/bpf/tracex4_user.c b/samples/bpf/tracex4_user.c index bc4a3bdea6ed..03449f773cb1 100644 --- a/samples/bpf/tracex4_user.c +++ b/samples/bpf/tracex4_user.c @@ -37,8 +37,8 @@ static void print_old_objects(int fd) key = write(1, "\e[1;1H\e[2J", 12); /* clear screen */ key = -1; - while (bpf_get_next_key(map_fd[0], &key, &next_key) == 0) { - bpf_lookup_elem(map_fd[0], &next_key, &v); + while (bpf_map_get_next_key(map_fd[0], &key, &next_key) == 0) { + bpf_map_lookup_elem(map_fd[0], &next_key, &v); key = next_key; if (val - v.val < 1000000000ll) /* object was allocated more then 1 sec ago */ diff --git a/samples/bpf/tracex6_user.c b/samples/bpf/tracex6_user.c index 8ea4976cfcf1..ca7874ed77f4 100644 --- a/samples/bpf/tracex6_user.c +++ b/samples/bpf/tracex6_user.c @@ -10,6 +10,7 @@ #include <linux/bpf.h> #include "libbpf.h" #include "bpf_load.h" +#include "perf-sys.h" #define SAMPLE_PERIOD 0x7fffffffffffffffULL @@ -30,13 +31,13 @@ static void test_bpf_perf_event(void) }; for (i = 0; i < nr_cpus; i++) { - pmu_fd[i] = perf_event_open(&attr_insn_pmu, -1/*pid*/, i/*cpu*/, -1/*group_fd*/, 0); + pmu_fd[i] = sys_perf_event_open(&attr_insn_pmu, -1/*pid*/, i/*cpu*/, -1/*group_fd*/, 0); if (pmu_fd[i] < 0) { printf("event syscall failed\n"); goto exit; } - bpf_update_elem(map_fd[0], &i, &pmu_fd[i], BPF_ANY); + bpf_map_update_elem(map_fd[0], &i, &pmu_fd[i], BPF_ANY); ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0); } diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index 5f040a0d7712..d2be65d1fd86 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -43,7 +43,7 @@ static void poll_stats(int interval) for (key = 0; key < nr_keys; key++) { __u64 sum = 0; - assert(bpf_lookup_elem(map_fd[0], &key, values) == 0); + assert(bpf_map_lookup_elem(map_fd[0], &key, values) == 0); for (i = 0; i < nr_cpus; i++) sum += (values[i] - prev[key][i]); if (sum) diff --git a/samples/bpf/xdp_tx_iptunnel_kern.c b/samples/bpf/xdp_tx_iptunnel_kern.c index 85c38ecd3a2d..0f4f6e8c8611 100644 --- a/samples/bpf/xdp_tx_iptunnel_kern.c +++ b/samples/bpf/xdp_tx_iptunnel_kern.c @@ -8,6 +8,7 @@ * encapsulating the incoming packet in an IPv4/v6 header * and then XDP_TX it out. */ +#define KBUILD_MODNAME "foo" #include <uapi/linux/bpf.h> #include <linux/in.h> #include <linux/if_ether.h> diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 7a71f5c74684..70e192fc61aa 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -51,7 +51,7 @@ static void poll_stats(unsigned int kill_after_s) for (proto = 0; proto < nr_protos; proto++) { __u64 sum = 0; - assert(bpf_lookup_elem(map_fd[0], &proto, values) == 0); + assert(bpf_map_lookup_elem(map_fd[0], &proto, values) == 0); for (i = 0; i < nr_cpus; i++) sum += (values[i] - prev[proto][i]); @@ -237,8 +237,8 @@ int main(int argc, char **argv) while (min_port <= max_port) { vip.dport = htons(min_port++); - if (bpf_update_elem(map_fd[1], &vip, &tnl, BPF_NOEXIST)) { - perror("bpf_update_elem(&vip2tnl)"); + if (bpf_map_update_elem(map_fd[1], &vip, &tnl, BPF_NOEXIST)) { + perror("bpf_map_update_elem(&vip2tnl)"); return 1; } } diff --git a/samples/connector/Makefile b/samples/connector/Makefile index 04b9622b6f51..91762d946a53 100644 --- a/samples/connector/Makefile +++ b/samples/connector/Makefile @@ -13,4 +13,4 @@ HOSTCFLAGS_ucon.o += -I$(objtree)/usr/include all: modules modules clean: - $(MAKE) -C ../.. SUBDIRS=$(PWD) $@ + $(MAKE) -C ../.. SUBDIRS=$(CURDIR) $@ diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index ae7ff6f24f36..bf7cc6b0dc19 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -36,13 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG) HOSTLOADLIBES_bpf-fancy += $(MFLAG) HOSTLOADLIBES_dropper += $(MFLAG) endif -always := $(hostprogs-y) +always := $(hostprogs-m) else # MIPS system calls are defined based on the -mabi that is passed # to the toolchain which may or may not be a valid option # for the host toolchain. So disable tests if target architecture # is MIPS but the host isn't. ifndef CONFIG_MIPS -always := $(hostprogs-y) +always := $(hostprogs-m) endif endif diff --git a/samples/seccomp/bpf-helper.c b/samples/seccomp/bpf-helper.c index 05cb4d5ff9f5..1ef0f4d72898 100644 --- a/samples/seccomp/bpf-helper.c +++ b/samples/seccomp/bpf-helper.c @@ -18,41 +18,41 @@ int bpf_resolve_jumps(struct bpf_labels *labels, struct sock_filter *filter, size_t count) { - struct sock_filter *begin = filter; - __u8 insn = count - 1; + size_t i; - if (count < 1) + if (count < 1 || count > BPF_MAXINSNS) return -1; /* * Walk it once, backwards, to build the label table and do fixups. * Since backward jumps are disallowed by BPF, this is easy. */ - filter += insn; - for (; filter >= begin; --insn, --filter) { - if (filter->code != (BPF_JMP+BPF_JA)) + for (i = 0; i < count; ++i) { + size_t offset = count - i - 1; + struct sock_filter *instr = &filter[offset]; + if (instr->code != (BPF_JMP+BPF_JA)) continue; - switch ((filter->jt<<8)|filter->jf) { + switch ((instr->jt<<8)|instr->jf) { case (JUMP_JT<<8)|JUMP_JF: - if (labels->labels[filter->k].location == 0xffffffff) { + if (labels->labels[instr->k].location == 0xffffffff) { fprintf(stderr, "Unresolved label: '%s'\n", - labels->labels[filter->k].label); + labels->labels[instr->k].label); return 1; } - filter->k = labels->labels[filter->k].location - - (insn + 1); - filter->jt = 0; - filter->jf = 0; + instr->k = labels->labels[instr->k].location - + (offset + 1); + instr->jt = 0; + instr->jf = 0; continue; case (LABEL_JT<<8)|LABEL_JF: - if (labels->labels[filter->k].location != 0xffffffff) { + if (labels->labels[instr->k].location != 0xffffffff) { fprintf(stderr, "Duplicate label use: '%s'\n", - labels->labels[filter->k].label); + labels->labels[instr->k].label); return 1; } - labels->labels[filter->k].location = insn; - filter->k = 0; /* fall through */ - filter->jt = 0; - filter->jf = 0; + labels->labels[instr->k].location = offset; + instr->k = 0; /* fall through */ + instr->jt = 0; + instr->jf = 0; continue; } } diff --git a/samples/seccomp/dropper.c b/samples/seccomp/dropper.c index c69c347c7011..68325ca5e71c 100644 --- a/samples/seccomp/dropper.c +++ b/samples/seccomp/dropper.c @@ -11,7 +11,6 @@ * When run, returns the specified errno for the specified * system call number against the given architecture. * - * Run this one as root as PR_SET_NO_NEW_PRIVS is not called. */ #include <errno.h> @@ -42,8 +41,12 @@ static int install_filter(int nr, int arch, int error) .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .filter = filter, }; + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + return 1; + } if (prctl(PR_SET_SECCOMP, 2, &prog)) { - perror("prctl"); + perror("prctl(PR_SET_SECCOMP)"); return 1; } return 0; diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c index 880a7d1d27d2..30e282d33d4d 100644 --- a/samples/trace_events/trace-events-sample.c +++ b/samples/trace_events/trace-events-sample.c @@ -79,7 +79,7 @@ static int simple_thread_fn(void *arg) static DEFINE_MUTEX(thread_mutex); -void foo_bar_reg(void) +int foo_bar_reg(void) { pr_info("Starting thread for foo_bar_fn\n"); /* @@ -90,6 +90,7 @@ void foo_bar_reg(void) mutex_lock(&thread_mutex); simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); mutex_unlock(&thread_mutex); + return 0; } void foo_bar_unreg(void) diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index d6b75bb495b3..76a75ab7a608 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -354,7 +354,7 @@ TRACE_EVENT_CONDITION(foo_bar_with_cond, TP_printk("foo %s %d", __get_str(foo), __entry->bar) ); -void foo_bar_reg(void); +int foo_bar_reg(void); void foo_bar_unreg(void); /* diff --git a/samples/vfio-mdev/Makefile b/samples/vfio-mdev/Makefile new file mode 100644 index 000000000000..cbbd868a50a8 --- /dev/null +++ b/samples/vfio-mdev/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) += mtty.o diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c new file mode 100644 index 000000000000..ca495686b9c3 --- /dev/null +++ b/samples/vfio-mdev/mtty.c @@ -0,0 +1,1516 @@ +/* + * Mediated virtual PCI serial host device driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Author: Neo Jia <cjia@nvidia.com> + * Kirti Wankhede <kwankhede@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Sample driver that creates mdev device that simulates serial port over PCI + * card. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/uuid.h> +#include <linux/vfio.h> +#include <linux/iommu.h> +#include <linux/sysfs.h> +#include <linux/ctype.h> +#include <linux/file.h> +#include <linux/mdev.h> +#include <linux/pci.h> +#include <linux/serial.h> +#include <uapi/linux/serial_reg.h> +#include <linux/eventfd.h> +/* + * #defines + */ + +#define VERSION_STRING "0.1" +#define DRIVER_AUTHOR "NVIDIA Corporation" + +#define MTTY_CLASS_NAME "mtty" + +#define MTTY_NAME "mtty" + +#define MTTY_STRING_LEN 16 + +#define MTTY_CONFIG_SPACE_SIZE 0xff +#define MTTY_IO_BAR_SIZE 0x8 +#define MTTY_MMIO_BAR_SIZE 0x100000 + +#define STORE_LE16(addr, val) (*(u16 *)addr = val) +#define STORE_LE32(addr, val) (*(u32 *)addr = val) + +#define MAX_FIFO_SIZE 16 + +#define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) + +#define MTTY_VFIO_PCI_OFFSET_SHIFT 40 + +#define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT) +#define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ + ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT) +#define MTTY_VFIO_PCI_OFFSET_MASK \ + (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1) +#define MAX_MTTYS 24 + +/* + * Global Structures + */ + +struct mtty_dev { + dev_t vd_devt; + struct class *vd_class; + struct cdev vd_cdev; + struct idr vd_idr; + struct device dev; +} mtty_dev; + +struct mdev_region_info { + u64 start; + u64 phys_start; + u32 size; + u64 vfio_offset; +}; + +#if defined(DEBUG_REGS) +const char *wr_reg[] = { + "TX", + "IER", + "FCR", + "LCR", + "MCR", + "LSR", + "MSR", + "SCR" +}; + +const char *rd_reg[] = { + "RX", + "IER", + "IIR", + "LCR", + "MCR", + "LSR", + "MSR", + "SCR" +}; +#endif + +/* loop back buffer */ +struct rxtx { + u8 fifo[MAX_FIFO_SIZE]; + u8 head, tail; + u8 count; +}; + +struct serial_port { + u8 uart_reg[8]; /* 8 registers */ + struct rxtx rxtx; /* loop back buffer */ + bool dlab; + bool overrun; + u16 divisor; + u8 fcr; /* FIFO control register */ + u8 max_fifo_size; + u8 intr_trigger_level; /* interrupt trigger level */ +}; + +/* State of each mdev device */ +struct mdev_state { + int irq_fd; + struct eventfd_ctx *intx_evtfd; + struct eventfd_ctx *msi_evtfd; + int irq_index; + u8 *vconfig; + struct mutex ops_lock; + struct mdev_device *mdev; + struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; + u32 bar_mask[VFIO_PCI_NUM_REGIONS]; + struct list_head next; + struct serial_port s[2]; + struct mutex rxtx_lock; + struct vfio_device_info dev_info; + int nr_ports; +}; + +struct mutex mdev_list_lock; +struct list_head mdev_devices_list; + +static const struct file_operations vd_fops = { + .owner = THIS_MODULE, +}; + +/* function prototypes */ + +static int mtty_trigger_interrupt(uuid_le uuid); + +/* Helper functions */ +static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid) +{ + struct mdev_state *mds; + + list_for_each_entry(mds, &mdev_devices_list, next) { + if (uuid_le_cmp(mdev_uuid(mds->mdev), uuid) == 0) + return mds; + } + + return NULL; +} + +void dump_buffer(char *buf, uint32_t count) +{ +#if defined(DEBUG) + int i; + + pr_info("Buffer:\n"); + for (i = 0; i < count; i++) { + pr_info("%2x ", *(buf + i)); + if ((i + 1) % 16 == 0) + pr_info("\n"); + } +#endif +} + +static void mtty_create_config_space(struct mdev_state *mdev_state) +{ + /* PCI dev ID */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); + + /* Control: I/O+, Mem-, BusMaster- */ + STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); + + /* Status: capabilities list absent */ + STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); + + /* Rev ID */ + mdev_state->vconfig[0x8] = 0x10; + + /* programming interface class : 16550-compatible serial controller */ + mdev_state->vconfig[0x9] = 0x02; + + /* Sub class : 00 */ + mdev_state->vconfig[0xa] = 0x00; + + /* Base class : Simple Communication controllers */ + mdev_state->vconfig[0xb] = 0x07; + + /* base address registers */ + /* BAR0: IO space */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); + mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; + + if (mdev_state->nr_ports == 2) { + /* BAR1: IO space */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); + mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; + } + + /* Subsystem ID */ + STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); + + mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ + mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ + + /* Vendor specific data */ + mdev_state->vconfig[0x40] = 0x23; + mdev_state->vconfig[0x43] = 0x80; + mdev_state->vconfig[0x44] = 0x23; + mdev_state->vconfig[0x48] = 0x23; + mdev_state->vconfig[0x4c] = 0x23; + + mdev_state->vconfig[0x60] = 0x50; + mdev_state->vconfig[0x61] = 0x43; + mdev_state->vconfig[0x62] = 0x49; + mdev_state->vconfig[0x63] = 0x20; + mdev_state->vconfig[0x64] = 0x53; + mdev_state->vconfig[0x65] = 0x65; + mdev_state->vconfig[0x66] = 0x72; + mdev_state->vconfig[0x67] = 0x69; + mdev_state->vconfig[0x68] = 0x61; + mdev_state->vconfig[0x69] = 0x6c; + mdev_state->vconfig[0x6a] = 0x2f; + mdev_state->vconfig[0x6b] = 0x55; + mdev_state->vconfig[0x6c] = 0x41; + mdev_state->vconfig[0x6d] = 0x52; + mdev_state->vconfig[0x6e] = 0x54; +} + +static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, + char *buf, u32 count) +{ + u32 cfg_addr, bar_mask, bar_index = 0; + + switch (offset) { + case 0x04: /* device control */ + case 0x06: /* device status */ + /* do nothing */ + break; + case 0x3c: /* interrupt line */ + mdev_state->vconfig[0x3c] = buf[0]; + break; + case 0x3d: + /* + * Interrupt Pin is hardwired to INTA. + * This field is write protected by hardware + */ + break; + case 0x10: /* BAR0 */ + case 0x14: /* BAR1 */ + if (offset == 0x10) + bar_index = 0; + else if (offset == 0x14) + bar_index = 1; + + if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { + STORE_LE32(&mdev_state->vconfig[offset], 0); + break; + } + + cfg_addr = *(u32 *)buf; + pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); + + if (cfg_addr == 0xffffffff) { + bar_mask = mdev_state->bar_mask[bar_index]; + cfg_addr = (cfg_addr & bar_mask); + } + + cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); + STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); + break; + case 0x18: /* BAR2 */ + case 0x1c: /* BAR3 */ + case 0x20: /* BAR4 */ + STORE_LE32(&mdev_state->vconfig[offset], 0); + break; + default: + pr_info("PCI config write @0x%x of %d bytes not handled\n", + offset, count); + break; + } +} + +static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, + u16 offset, char *buf, u32 count) +{ + u8 data = *buf; + + /* Handle data written by guest */ + switch (offset) { + case UART_TX: + /* if DLAB set, data is LSB of divisor */ + if (mdev_state->s[index].dlab) { + mdev_state->s[index].divisor |= data; + break; + } + + mutex_lock(&mdev_state->rxtx_lock); + + /* save in TX buffer */ + if (mdev_state->s[index].rxtx.count < + mdev_state->s[index].max_fifo_size) { + mdev_state->s[index].rxtx.fifo[ + mdev_state->s[index].rxtx.head] = data; + mdev_state->s[index].rxtx.count++; + CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); + mdev_state->s[index].overrun = false; + + /* + * Trigger interrupt if receive data interrupt is + * enabled and fifo reached trigger level + */ + if ((mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_RDI) && + (mdev_state->s[index].rxtx.count == + mdev_state->s[index].intr_trigger_level)) { + /* trigger interrupt */ +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Fifo level trigger\n", + index); +#endif + mtty_trigger_interrupt( + mdev_uuid(mdev_state->mdev)); + } + } else { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Buffer Overflow\n", index); +#endif + mdev_state->s[index].overrun = true; + + /* + * Trigger interrupt if receiver line status interrupt + * is enabled + */ + if (mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_RLSI) + mtty_trigger_interrupt( + mdev_uuid(mdev_state->mdev)); + } + mutex_unlock(&mdev_state->rxtx_lock); + break; + + case UART_IER: + /* if DLAB set, data is MSB of divisor */ + if (mdev_state->s[index].dlab) + mdev_state->s[index].divisor |= (u16)data << 8; + else { + mdev_state->s[index].uart_reg[offset] = data; + mutex_lock(&mdev_state->rxtx_lock); + if ((data & UART_IER_THRI) && + (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail)) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: IER_THRI write\n", + index); +#endif + mtty_trigger_interrupt( + mdev_uuid(mdev_state->mdev)); + } + + mutex_unlock(&mdev_state->rxtx_lock); + } + + break; + + case UART_FCR: + mdev_state->s[index].fcr = data; + + mutex_lock(&mdev_state->rxtx_lock); + if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { + /* clear loop back FIFO */ + mdev_state->s[index].rxtx.count = 0; + mdev_state->s[index].rxtx.head = 0; + mdev_state->s[index].rxtx.tail = 0; + } + mutex_unlock(&mdev_state->rxtx_lock); + + switch (data & UART_FCR_TRIGGER_MASK) { + case UART_FCR_TRIGGER_1: + mdev_state->s[index].intr_trigger_level = 1; + break; + + case UART_FCR_TRIGGER_4: + mdev_state->s[index].intr_trigger_level = 4; + break; + + case UART_FCR_TRIGGER_8: + mdev_state->s[index].intr_trigger_level = 8; + break; + + case UART_FCR_TRIGGER_14: + mdev_state->s[index].intr_trigger_level = 14; + break; + } + + /* + * Set trigger level to 1 otherwise or implement timer with + * timeout of 4 characters and on expiring that timer set + * Recevice data timeout in IIR register + */ + mdev_state->s[index].intr_trigger_level = 1; + if (data & UART_FCR_ENABLE_FIFO) + mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; + else { + mdev_state->s[index].max_fifo_size = 1; + mdev_state->s[index].intr_trigger_level = 1; + } + + break; + + case UART_LCR: + if (data & UART_LCR_DLAB) { + mdev_state->s[index].dlab = true; + mdev_state->s[index].divisor = 0; + } else + mdev_state->s[index].dlab = false; + + mdev_state->s[index].uart_reg[offset] = data; + break; + + case UART_MCR: + mdev_state->s[index].uart_reg[offset] = data; + + if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && + (data & UART_MCR_OUT2)) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: MCR_OUT2 write\n", index); +#endif + mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); + } + + if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) && + (data & (UART_MCR_RTS | UART_MCR_DTR))) { +#if defined(DEBUG_INTR) + pr_err("Serial port %d: MCR RTS/DTR write\n", index); +#endif + mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev)); + } + break; + + case UART_LSR: + case UART_MSR: + /* do nothing */ + break; + + case UART_SCR: + mdev_state->s[index].uart_reg[offset] = data; + break; + + default: + break; + } +} + +static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state, + u16 offset, char *buf, u32 count) +{ + /* Handle read requests by guest */ + switch (offset) { + case UART_RX: + /* if DLAB set, data is LSB of divisor */ + if (mdev_state->s[index].dlab) { + *buf = (u8)mdev_state->s[index].divisor; + break; + } + + mutex_lock(&mdev_state->rxtx_lock); + /* return data in tx buffer */ + if (mdev_state->s[index].rxtx.head != + mdev_state->s[index].rxtx.tail) { + *buf = mdev_state->s[index].rxtx.fifo[ + mdev_state->s[index].rxtx.tail]; + mdev_state->s[index].rxtx.count--; + CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.tail); + } + + if (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail) { + /* + * Trigger interrupt if tx buffer empty interrupt is + * enabled and fifo is empty + */ +#if defined(DEBUG_INTR) + pr_err("Serial port %d: Buffer Empty\n", index); +#endif + if (mdev_state->s[index].uart_reg[UART_IER] & + UART_IER_THRI) + mtty_trigger_interrupt( + mdev_uuid(mdev_state->mdev)); + } + mutex_unlock(&mdev_state->rxtx_lock); + + break; + + case UART_IER: + if (mdev_state->s[index].dlab) { + *buf = (u8)(mdev_state->s[index].divisor >> 8); + break; + } + *buf = mdev_state->s[index].uart_reg[offset] & 0x0f; + break; + + case UART_IIR: + { + u8 ier = mdev_state->s[index].uart_reg[UART_IER]; + *buf = 0; + + mutex_lock(&mdev_state->rxtx_lock); + /* Interrupt priority 1: Parity, overrun, framing or break */ + if ((ier & UART_IER_RLSI) && mdev_state->s[index].overrun) + *buf |= UART_IIR_RLSI; + + /* Interrupt priority 2: Fifo trigger level reached */ + if ((ier & UART_IER_RDI) && + (mdev_state->s[index].rxtx.count == + mdev_state->s[index].intr_trigger_level)) + *buf |= UART_IIR_RDI; + + /* Interrupt priotiry 3: transmitter holding register empty */ + if ((ier & UART_IER_THRI) && + (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail)) + *buf |= UART_IIR_THRI; + + /* Interrupt priotiry 4: Modem status: CTS, DSR, RI or DCD */ + if ((ier & UART_IER_MSI) && + (mdev_state->s[index].uart_reg[UART_MCR] & + (UART_MCR_RTS | UART_MCR_DTR))) + *buf |= UART_IIR_MSI; + + /* bit0: 0=> interrupt pending, 1=> no interrupt is pending */ + if (*buf == 0) + *buf = UART_IIR_NO_INT; + + /* set bit 6 & 7 to be 16550 compatible */ + *buf |= 0xC0; + mutex_unlock(&mdev_state->rxtx_lock); + } + break; + + case UART_LCR: + case UART_MCR: + *buf = mdev_state->s[index].uart_reg[offset]; + break; + + case UART_LSR: + { + u8 lsr = 0; + + mutex_lock(&mdev_state->rxtx_lock); + /* atleast one char in FIFO */ + if (mdev_state->s[index].rxtx.head != + mdev_state->s[index].rxtx.tail) + lsr |= UART_LSR_DR; + + /* if FIFO overrun */ + if (mdev_state->s[index].overrun) + lsr |= UART_LSR_OE; + + /* transmit FIFO empty and tramsitter empty */ + if (mdev_state->s[index].rxtx.head == + mdev_state->s[index].rxtx.tail) + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + + mutex_unlock(&mdev_state->rxtx_lock); + *buf = lsr; + break; + } + case UART_MSR: + *buf = UART_MSR_DSR | UART_MSR_DDSR | UART_MSR_DCD; + + mutex_lock(&mdev_state->rxtx_lock); + /* if AFE is 1 and FIFO have space, set CTS bit */ + if (mdev_state->s[index].uart_reg[UART_MCR] & + UART_MCR_AFE) { + if (mdev_state->s[index].rxtx.count < + mdev_state->s[index].max_fifo_size) + *buf |= UART_MSR_CTS | UART_MSR_DCTS; + } else + *buf |= UART_MSR_CTS | UART_MSR_DCTS; + mutex_unlock(&mdev_state->rxtx_lock); + + break; + + case UART_SCR: + *buf = mdev_state->s[index].uart_reg[offset]; + break; + + default: + break; + } +} + +static void mdev_read_base(struct mdev_state *mdev_state) +{ + int index, pos; + u32 start_lo, start_hi; + u32 mem_type; + + pos = PCI_BASE_ADDRESS_0; + + for (index = 0; index <= VFIO_PCI_BAR5_REGION_INDEX; index++) { + + if (!mdev_state->region_info[index].size) + continue; + + start_lo = (*(u32 *)(mdev_state->vconfig + pos)) & + PCI_BASE_ADDRESS_MEM_MASK; + mem_type = (*(u32 *)(mdev_state->vconfig + pos)) & + PCI_BASE_ADDRESS_MEM_TYPE_MASK; + + switch (mem_type) { + case PCI_BASE_ADDRESS_MEM_TYPE_64: + start_hi = (*(u32 *)(mdev_state->vconfig + pos + 4)); + pos += 4; + break; + case PCI_BASE_ADDRESS_MEM_TYPE_32: + case PCI_BASE_ADDRESS_MEM_TYPE_1M: + /* 1M mem BAR treated as 32-bit BAR */ + default: + /* mem unknown type treated as 32-bit BAR */ + start_hi = 0; + break; + } + pos += 4; + mdev_state->region_info[index].start = ((u64)start_hi << 32) | + start_lo; + } +} + +static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, + loff_t pos, bool is_write) +{ + struct mdev_state *mdev_state; + unsigned int index; + loff_t offset; + int ret = 0; + + if (!mdev || !buf) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) { + pr_err("%s mdev_state not found\n", __func__); + return -EINVAL; + } + + mutex_lock(&mdev_state->ops_lock); + + index = MTTY_VFIO_PCI_OFFSET_TO_INDEX(pos); + offset = pos & MTTY_VFIO_PCI_OFFSET_MASK; + switch (index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + +#if defined(DEBUG) + pr_info("%s: PCI config space %s at offset 0x%llx\n", + __func__, is_write ? "write" : "read", offset); +#endif + if (is_write) { + dump_buffer(buf, count); + handle_pci_cfg_write(mdev_state, offset, buf, count); + } else { + memcpy(buf, (mdev_state->vconfig + offset), count); + dump_buffer(buf, count); + } + + break; + + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + if (!mdev_state->region_info[index].start) + mdev_read_base(mdev_state); + + if (is_write) { + dump_buffer(buf, count); + +#if defined(DEBUG_REGS) + pr_info("%s: BAR%d WR @0x%llx %s val:0x%02x dlab:%d\n", + __func__, index, offset, wr_reg[offset], + (u8)*buf, mdev_state->s[index].dlab); +#endif + handle_bar_write(index, mdev_state, offset, buf, count); + } else { + handle_bar_read(index, mdev_state, offset, buf, count); + dump_buffer(buf, count); + +#if defined(DEBUG_REGS) + pr_info("%s: BAR%d RD @0x%llx %s val:0x%02x dlab:%d\n", + __func__, index, offset, rd_reg[offset], + (u8)*buf, mdev_state->s[index].dlab); +#endif + } + break; + + default: + ret = -1; + goto accessfailed; + } + + ret = count; + + +accessfailed: + mutex_unlock(&mdev_state->ops_lock); + + return ret; +} + +int mtty_create(struct kobject *kobj, struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + char name[MTTY_STRING_LEN]; + int nr_ports = 0, i; + + if (!mdev) + return -EINVAL; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(mdev_parent_dev(mdev)), i + 1); + if (!strcmp(kobj->name, name)) { + nr_ports = i + 1; + break; + } + } + + if (!nr_ports) + return -EINVAL; + + mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); + if (mdev_state == NULL) + return -ENOMEM; + + mdev_state->nr_ports = nr_ports; + mdev_state->irq_index = -1; + mdev_state->s[0].max_fifo_size = MAX_FIFO_SIZE; + mdev_state->s[1].max_fifo_size = MAX_FIFO_SIZE; + mutex_init(&mdev_state->rxtx_lock); + mdev_state->vconfig = kzalloc(MTTY_CONFIG_SPACE_SIZE, GFP_KERNEL); + + if (mdev_state->vconfig == NULL) { + kfree(mdev_state); + return -ENOMEM; + } + + mutex_init(&mdev_state->ops_lock); + mdev_state->mdev = mdev; + mdev_set_drvdata(mdev, mdev_state); + + mtty_create_config_space(mdev_state); + + mutex_lock(&mdev_list_lock); + list_add(&mdev_state->next, &mdev_devices_list); + mutex_unlock(&mdev_list_lock); + + return 0; +} + +int mtty_remove(struct mdev_device *mdev) +{ + struct mdev_state *mds, *tmp_mds; + struct mdev_state *mdev_state = mdev_get_drvdata(mdev); + int ret = -EINVAL; + + mutex_lock(&mdev_list_lock); + list_for_each_entry_safe(mds, tmp_mds, &mdev_devices_list, next) { + if (mdev_state == mds) { + list_del(&mdev_state->next); + mdev_set_drvdata(mdev, NULL); + kfree(mdev_state->vconfig); + kfree(mdev_state); + ret = 0; + break; + } + } + mutex_unlock(&mdev_list_lock); + + return ret; +} + +int mtty_reset(struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + pr_info("%s: called\n", __func__); + + return 0; +} + +ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 2; + } else { + u8 val; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, false); + if (ret <= 0) + goto read_err; + + if (copy_to_user(buf, &val, sizeof(val))) + goto read_err; + + filled = 1; + } + + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; + +read_err: + return -EFAULT; +} + +ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int done = 0; + int ret; + + while (count) { + size_t filled; + + if (count >= 4 && !(*ppos % 4)) { + u32 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 4; + } else if (count >= 2 && !(*ppos % 2)) { + u16 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 2; + } else { + u8 val; + + if (copy_from_user(&val, buf, sizeof(val))) + goto write_err; + + ret = mdev_access(mdev, (char *)&val, sizeof(val), + *ppos, true); + if (ret <= 0) + goto write_err; + + filled = 1; + } + count -= filled; + done += filled; + *ppos += filled; + buf += filled; + } + + return done; +write_err: + return -EFAULT; +} + +static int mtty_set_irqs(struct mdev_device *mdev, uint32_t flags, + unsigned int index, unsigned int start, + unsigned int count, void *data) +{ + int ret = 0; + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + mutex_lock(&mdev_state->ops_lock); + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + { + if (flags & VFIO_IRQ_SET_DATA_NONE) { + pr_info("%s: disable INTx\n", __func__); + if (mdev_state->intx_evtfd) + eventfd_ctx_put(mdev_state->intx_evtfd); + break; + } + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int fd = *(int *)data; + + if (fd > 0) { + struct eventfd_ctx *evt; + + evt = eventfd_ctx_fdget(fd); + if (IS_ERR(evt)) { + ret = PTR_ERR(evt); + break; + } + mdev_state->intx_evtfd = evt; + mdev_state->irq_fd = fd; + mdev_state->irq_index = index; + break; + } + } + break; + } + } + break; + case VFIO_PCI_MSI_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + if (flags & VFIO_IRQ_SET_DATA_NONE) { + if (mdev_state->msi_evtfd) + eventfd_ctx_put(mdev_state->msi_evtfd); + pr_info("%s: disable MSI\n", __func__); + mdev_state->irq_index = VFIO_PCI_INTX_IRQ_INDEX; + break; + } + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int fd = *(int *)data; + struct eventfd_ctx *evt; + + if (fd <= 0) + break; + + if (mdev_state->msi_evtfd) + break; + + evt = eventfd_ctx_fdget(fd); + if (IS_ERR(evt)) { + ret = PTR_ERR(evt); + break; + } + mdev_state->msi_evtfd = evt; + mdev_state->irq_fd = fd; + mdev_state->irq_index = index; + } + break; + } + break; + case VFIO_PCI_MSIX_IRQ_INDEX: + pr_info("%s: MSIX_IRQ\n", __func__); + break; + case VFIO_PCI_ERR_IRQ_INDEX: + pr_info("%s: ERR_IRQ\n", __func__); + break; + case VFIO_PCI_REQ_IRQ_INDEX: + pr_info("%s: REQ_IRQ\n", __func__); + break; + } + + mutex_unlock(&mdev_state->ops_lock); + return ret; +} + +static int mtty_trigger_interrupt(uuid_le uuid) +{ + int ret = -1; + struct mdev_state *mdev_state; + + mdev_state = find_mdev_state_by_uuid(uuid); + + if (!mdev_state) { + pr_info("%s: mdev not found\n", __func__); + return -EINVAL; + } + + if ((mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX) && + (!mdev_state->msi_evtfd)) + return -EINVAL; + else if ((mdev_state->irq_index == VFIO_PCI_INTX_IRQ_INDEX) && + (!mdev_state->intx_evtfd)) { + pr_info("%s: Intr eventfd not found\n", __func__); + return -EINVAL; + } + + if (mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX) + ret = eventfd_signal(mdev_state->msi_evtfd, 1); + else + ret = eventfd_signal(mdev_state->intx_evtfd, 1); + +#if defined(DEBUG_INTR) + pr_info("Intx triggered\n"); +#endif + if (ret != 1) + pr_err("%s: eventfd signal failed (%d)\n", __func__, ret); + + return ret; +} + +int mtty_get_region_info(struct mdev_device *mdev, + struct vfio_region_info *region_info, + u16 *cap_type_id, void **cap_type) +{ + unsigned int size = 0; + struct mdev_state *mdev_state; + u32 bar_index; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -EINVAL; + + bar_index = region_info->index; + if (bar_index >= VFIO_PCI_NUM_REGIONS) + return -EINVAL; + + mutex_lock(&mdev_state->ops_lock); + + switch (bar_index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + size = MTTY_CONFIG_SPACE_SIZE; + break; + case VFIO_PCI_BAR0_REGION_INDEX: + size = MTTY_IO_BAR_SIZE; + break; + case VFIO_PCI_BAR1_REGION_INDEX: + if (mdev_state->nr_ports == 2) + size = MTTY_IO_BAR_SIZE; + break; + default: + size = 0; + break; + } + + mdev_state->region_info[bar_index].size = size; + mdev_state->region_info[bar_index].vfio_offset = + MTTY_VFIO_PCI_INDEX_TO_OFFSET(bar_index); + + region_info->size = size; + region_info->offset = MTTY_VFIO_PCI_INDEX_TO_OFFSET(bar_index); + region_info->flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + mutex_unlock(&mdev_state->ops_lock); + return 0; +} + +int mtty_get_irq_info(struct mdev_device *mdev, struct vfio_irq_info *irq_info) +{ + switch (irq_info->index) { + case VFIO_PCI_INTX_IRQ_INDEX: + case VFIO_PCI_MSI_IRQ_INDEX: + case VFIO_PCI_REQ_IRQ_INDEX: + break; + + default: + return -EINVAL; + } + + irq_info->flags = VFIO_IRQ_INFO_EVENTFD; + irq_info->count = 1; + + if (irq_info->index == VFIO_PCI_INTX_IRQ_INDEX) + irq_info->flags |= (VFIO_IRQ_INFO_MASKABLE | + VFIO_IRQ_INFO_AUTOMASKED); + else + irq_info->flags |= VFIO_IRQ_INFO_NORESIZE; + + return 0; +} + +int mtty_get_device_info(struct mdev_device *mdev, + struct vfio_device_info *dev_info) +{ + dev_info->flags = VFIO_DEVICE_FLAGS_PCI; + dev_info->num_regions = VFIO_PCI_NUM_REGIONS; + dev_info->num_irqs = VFIO_PCI_NUM_IRQS; + + return 0; +} + +static long mtty_ioctl(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + unsigned long minsz; + struct mdev_state *mdev_state; + + if (!mdev) + return -EINVAL; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -ENODEV; + + switch (cmd) { + case VFIO_DEVICE_GET_INFO: + { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + ret = mtty_get_device_info(mdev, &info); + if (ret) + return ret; + + memcpy(&mdev_state->dev_info, &info, sizeof(info)); + + if (copy_to_user((void __user *)arg, &info, minsz)) + return -EFAULT; + + return 0; + } + case VFIO_DEVICE_GET_REGION_INFO: + { + struct vfio_region_info info; + u16 cap_type_id = 0; + void *cap_type = NULL; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + ret = mtty_get_region_info(mdev, &info, &cap_type_id, + &cap_type); + if (ret) + return ret; + + if (copy_to_user((void __user *)arg, &info, minsz)) + return -EFAULT; + + return 0; + } + + case VFIO_DEVICE_GET_IRQ_INFO: + { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if ((info.argsz < minsz) || + (info.index >= mdev_state->dev_info.num_irqs)) + return -EINVAL; + + ret = mtty_get_irq_info(mdev, &info); + if (ret) + return ret; + + if (copy_to_user((void __user *)arg, &info, minsz)) + return -EFAULT; + + return 0; + } + case VFIO_DEVICE_SET_IRQS: + { + struct vfio_irq_set hdr; + u8 *data = NULL, *ptr = NULL; + size_t data_size = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + ret = vfio_set_irqs_validate_and_prepare(&hdr, + mdev_state->dev_info.num_irqs, + VFIO_PCI_NUM_IRQS, + &data_size); + if (ret) + return ret; + + if (data_size) { + ptr = data = memdup_user((void __user *)(arg + minsz), + data_size); + if (IS_ERR(data)) + return PTR_ERR(data); + } + + ret = mtty_set_irqs(mdev, hdr.flags, hdr.index, hdr.start, + hdr.count, data); + + kfree(ptr); + return ret; + } + case VFIO_DEVICE_RESET: + return mtty_reset(mdev); + } + return -ENOTTY; +} + +int mtty_open(struct mdev_device *mdev) +{ + pr_info("%s\n", __func__); + return 0; +} + +void mtty_close(struct mdev_device *mdev) +{ + pr_info("%s\n", __func__); +} + +static ssize_t +sample_mtty_dev_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "This is phy device\n"); +} + +static DEVICE_ATTR_RO(sample_mtty_dev); + +static struct attribute *mtty_dev_attrs[] = { + &dev_attr_sample_mtty_dev.attr, + NULL, +}; + +static const struct attribute_group mtty_dev_group = { + .name = "mtty_dev", + .attrs = mtty_dev_attrs, +}; + +const struct attribute_group *mtty_dev_groups[] = { + &mtty_dev_group, + NULL, +}; + +static ssize_t +sample_mdev_dev_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (mdev_from_dev(dev)) + return sprintf(buf, "This is MDEV %s\n", dev_name(dev)); + + return sprintf(buf, "\n"); +} + +static DEVICE_ATTR_RO(sample_mdev_dev); + +static struct attribute *mdev_dev_attrs[] = { + &dev_attr_sample_mdev_dev.attr, + NULL, +}; + +static const struct attribute_group mdev_dev_group = { + .name = "vendor", + .attrs = mdev_dev_attrs, +}; + +const struct attribute_group *mdev_dev_groups[] = { + &mdev_dev_group, + NULL, +}; + +static ssize_t +name_show(struct kobject *kobj, struct device *dev, char *buf) +{ + char name[MTTY_STRING_LEN]; + int i; + const char *name_str[2] = {"Single port serial", "Dual port serial"}; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(dev), i + 1); + if (!strcmp(kobj->name, name)) + return sprintf(buf, "%s\n", name_str[i]); + } + + return -EINVAL; +} + +MDEV_TYPE_ATTR_RO(name); + +static ssize_t +available_instances_show(struct kobject *kobj, struct device *dev, char *buf) +{ + char name[MTTY_STRING_LEN]; + int i; + struct mdev_state *mds; + int ports = 0, used = 0; + + for (i = 0; i < 2; i++) { + snprintf(name, MTTY_STRING_LEN, "%s-%d", + dev_driver_string(dev), i + 1); + if (!strcmp(kobj->name, name)) { + ports = i + 1; + break; + } + } + + if (!ports) + return -EINVAL; + + list_for_each_entry(mds, &mdev_devices_list, next) + used += mds->nr_ports; + + return sprintf(buf, "%d\n", (MAX_MTTYS - used)/ports); +} + +MDEV_TYPE_ATTR_RO(available_instances); + + +static ssize_t device_api_show(struct kobject *kobj, struct device *dev, + char *buf) +{ + return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); +} + +MDEV_TYPE_ATTR_RO(device_api); + +static struct attribute *mdev_types_attrs[] = { + &mdev_type_attr_name.attr, + &mdev_type_attr_device_api.attr, + &mdev_type_attr_available_instances.attr, + NULL, +}; + +static struct attribute_group mdev_type_group1 = { + .name = "1", + .attrs = mdev_types_attrs, +}; + +static struct attribute_group mdev_type_group2 = { + .name = "2", + .attrs = mdev_types_attrs, +}; + +struct attribute_group *mdev_type_groups[] = { + &mdev_type_group1, + &mdev_type_group2, + NULL, +}; + +struct mdev_parent_ops mdev_fops = { + .owner = THIS_MODULE, + .dev_attr_groups = mtty_dev_groups, + .mdev_attr_groups = mdev_dev_groups, + .supported_type_groups = mdev_type_groups, + .create = mtty_create, + .remove = mtty_remove, + .open = mtty_open, + .release = mtty_close, + .read = mtty_read, + .write = mtty_write, + .ioctl = mtty_ioctl, +}; + +static void mtty_device_release(struct device *dev) +{ + dev_dbg(dev, "mtty: released\n"); +} + +static int __init mtty_dev_init(void) +{ + int ret = 0; + + pr_info("mtty_dev: %s\n", __func__); + + memset(&mtty_dev, 0, sizeof(mtty_dev)); + + idr_init(&mtty_dev.vd_idr); + + ret = alloc_chrdev_region(&mtty_dev.vd_devt, 0, MINORMASK, MTTY_NAME); + + if (ret < 0) { + pr_err("Error: failed to register mtty_dev, err:%d\n", ret); + return ret; + } + + cdev_init(&mtty_dev.vd_cdev, &vd_fops); + cdev_add(&mtty_dev.vd_cdev, mtty_dev.vd_devt, MINORMASK); + + pr_info("major_number:%d\n", MAJOR(mtty_dev.vd_devt)); + + mtty_dev.vd_class = class_create(THIS_MODULE, MTTY_CLASS_NAME); + + if (IS_ERR(mtty_dev.vd_class)) { + pr_err("Error: failed to register mtty_dev class\n"); + ret = PTR_ERR(mtty_dev.vd_class); + goto failed1; + } + + mtty_dev.dev.class = mtty_dev.vd_class; + mtty_dev.dev.release = mtty_device_release; + dev_set_name(&mtty_dev.dev, "%s", MTTY_NAME); + + ret = device_register(&mtty_dev.dev); + if (ret) + goto failed2; + + ret = mdev_register_device(&mtty_dev.dev, &mdev_fops); + if (ret) + goto failed3; + + mutex_init(&mdev_list_lock); + INIT_LIST_HEAD(&mdev_devices_list); + + goto all_done; + +failed3: + + device_unregister(&mtty_dev.dev); +failed2: + class_destroy(mtty_dev.vd_class); + +failed1: + cdev_del(&mtty_dev.vd_cdev); + unregister_chrdev_region(mtty_dev.vd_devt, MINORMASK); + +all_done: + return ret; +} + +static void __exit mtty_dev_exit(void) +{ + mtty_dev.dev.bus = NULL; + mdev_unregister_device(&mtty_dev.dev); + + device_unregister(&mtty_dev.dev); + idr_destroy(&mtty_dev.vd_idr); + cdev_del(&mtty_dev.vd_cdev); + unregister_chrdev_region(mtty_dev.vd_devt, MINORMASK); + class_destroy(mtty_dev.vd_class); + mtty_dev.vd_class = NULL; + pr_info("mtty_dev: Unloaded!\n"); +} + +module_init(mtty_dev_init) +module_exit(mtty_dev_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_INFO(supported, "Test driver that simulate serial port over PCI"); +MODULE_VERSION(VERSION_STRING); +MODULE_AUTHOR(DRIVER_AUTHOR); |