mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
66
samples/Kconfig
Normal file
66
samples/Kconfig
Normal file
|
@ -0,0 +1,66 @@
|
|||
menuconfig SAMPLES
|
||||
bool "Sample kernel code"
|
||||
help
|
||||
You can build and test sample kernel code here.
|
||||
|
||||
if SAMPLES
|
||||
|
||||
config SAMPLE_TRACE_EVENTS
|
||||
tristate "Build trace_events examples -- loadable modules only"
|
||||
depends on EVENT_TRACING && m
|
||||
help
|
||||
This build trace event example modules.
|
||||
|
||||
config SAMPLE_KOBJECT
|
||||
tristate "Build kobject examples -- loadable modules only"
|
||||
depends on m
|
||||
help
|
||||
This config option will allow you to build a number of
|
||||
different kobject sample modules showing how to use kobjects,
|
||||
ksets, and ktypes properly.
|
||||
|
||||
If in doubt, say "N" here.
|
||||
|
||||
config SAMPLE_KPROBES
|
||||
tristate "Build kprobes examples -- loadable modules only"
|
||||
depends on KPROBES && m
|
||||
help
|
||||
This build several kprobes example modules.
|
||||
|
||||
config SAMPLE_KRETPROBES
|
||||
tristate "Build kretprobes example -- loadable modules only"
|
||||
default m
|
||||
depends on SAMPLE_KPROBES && KRETPROBES
|
||||
|
||||
config SAMPLE_HW_BREAKPOINT
|
||||
tristate "Build kernel hardware breakpoint examples -- loadable module only"
|
||||
depends on HAVE_HW_BREAKPOINT && m
|
||||
help
|
||||
This builds kernel hardware breakpoint example modules.
|
||||
|
||||
config SAMPLE_KFIFO
|
||||
tristate "Build kfifo examples -- loadable modules only"
|
||||
depends on m
|
||||
help
|
||||
This config option will allow you to build a number of
|
||||
different kfifo sample modules showing how to use the
|
||||
generic kfifo API.
|
||||
|
||||
If in doubt, say "N" here.
|
||||
|
||||
config SAMPLE_KDB
|
||||
tristate "Build kdb command example -- loadable modules only"
|
||||
depends on KGDB_KDB && m
|
||||
help
|
||||
Build an example of how to dynamically add the hello
|
||||
command to the kdb shell.
|
||||
|
||||
config SAMPLE_RPMSG_CLIENT
|
||||
tristate "Build rpmsg client sample -- loadable modules only"
|
||||
depends on RPMSG && m
|
||||
help
|
||||
Build an rpmsg client sample driver, which demonstrates how
|
||||
to communicate with an AMP-configured remote processor over
|
||||
the rpmsg bus.
|
||||
|
||||
endif # SAMPLES
|
4
samples/Makefile
Normal file
4
samples/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Makefile for Linux samples code
|
||||
|
||||
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ \
|
||||
hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
|
12
samples/bpf/Makefile
Normal file
12
samples/bpf/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
||||
obj- := dummy.o
|
||||
|
||||
# List of programs to build
|
||||
hostprogs-y := test_verifier
|
||||
|
||||
test_verifier-objs := test_verifier.o libbpf.o
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
always := $(hostprogs-y)
|
||||
|
||||
HOSTCFLAGS += -I$(objtree)/usr/include
|
94
samples/bpf/libbpf.c
Normal file
94
samples/bpf/libbpf.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* 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 "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)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_type = map_type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_update_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_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)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
||||
bpf_log_buf[0] = 0;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
172
samples/bpf/libbpf.h
Normal file
172
samples/bpf/libbpf.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
/* eBPF mini library */
|
||||
#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 bpf_update_elem(int fd, void *key, void *value);
|
||||
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);
|
||||
|
||||
#define LOG_BUF_SIZE 8192
|
||||
extern char bpf_log_buf[LOG_BUF_SIZE];
|
||||
|
||||
/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
|
||||
|
||||
#define BPF_ALU64_REG(OP, DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_ALU32_REG(OP, DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_OP(OP) | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
|
||||
|
||||
#define BPF_ALU64_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_ALU32_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
/* Short form of mov, dst_reg = src_reg */
|
||||
|
||||
#define BPF_MOV64_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
/* Short form of mov, dst_reg = imm32 */
|
||||
|
||||
#define BPF_MOV64_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
|
||||
#define BPF_LD_IMM64(DST, IMM) \
|
||||
BPF_LD_IMM64_RAW(DST, 0, IMM)
|
||||
|
||||
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LD | BPF_DW | BPF_IMM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = (__u32) (IMM) }), \
|
||||
((struct bpf_insn) { \
|
||||
.code = 0, /* zero is reserved opcode */ \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = ((__u64) (IMM)) >> 32 })
|
||||
|
||||
#define BPF_PSEUDO_MAP_FD 1
|
||||
|
||||
/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
|
||||
#define BPF_LD_MAP_FD(DST, MAP_FD) \
|
||||
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
|
||||
|
||||
|
||||
/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
|
||||
|
||||
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
|
||||
|
||||
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
|
||||
|
||||
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
|
||||
|
||||
#define BPF_JMP_REG(OP, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
|
||||
|
||||
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
/* Raw code statement block */
|
||||
|
||||
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = CODE, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
/* Program exit */
|
||||
|
||||
#define BPF_EXIT_INSN() \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_EXIT, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#endif
|
689
samples/bpf/test_verifier.c
Normal file
689
samples/bpf/test_verifier.c
Normal file
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* Testsuite for eBPF verifier
|
||||
*
|
||||
* Copyright (c) 2014 PLUMgrid, http://plumgrid.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <string.h>
|
||||
#include <linux/filter.h>
|
||||
#include "libbpf.h"
|
||||
|
||||
#define MAX_INSNS 512
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
|
||||
struct bpf_test {
|
||||
const char *descr;
|
||||
struct bpf_insn insns[MAX_INSNS];
|
||||
int fixup[32];
|
||||
const char *errstr;
|
||||
enum {
|
||||
ACCEPT,
|
||||
REJECT
|
||||
} result;
|
||||
};
|
||||
|
||||
static struct bpf_test tests[] = {
|
||||
{
|
||||
"add+sub+mul",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 1),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 3),
|
||||
BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_2),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -1),
|
||||
BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 3),
|
||||
BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"unreachable",
|
||||
.insns = {
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "unreachable",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"unreachable2",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "unreachable",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"out of range jump",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "jump out of range",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"out of range jump2",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, -2),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "jump out of range",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"test1 ld_imm64",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid BPF_LD_IMM insn",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"test2 ld_imm64",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid BPF_LD_IMM insn",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"test3 ld_imm64",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
|
||||
BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 0),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_LD_IMM64(BPF_REG_0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_ld_imm64 insn",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"test4 ld_imm64",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_ld_imm64 insn",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"test5 ld_imm64",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
|
||||
},
|
||||
.errstr = "invalid bpf_ld_imm64 insn",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"no bpf_exit",
|
||||
.insns = {
|
||||
BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2),
|
||||
},
|
||||
.errstr = "jump out of range",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"loop (back-edge)",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "back-edge",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"loop2 (back-edge)",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, -4),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "back-edge",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"conditional loop",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "back-edge",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"read uninitialized register",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R2 !read_ok",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"read invalid register",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R15 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"program doesn't init R0 before exit",
|
||||
.insns = {
|
||||
BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R0 !read_ok",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"program doesn't init R0 before exit in all branches",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 1),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R0 !read_ok",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"stack out of bounds",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid stack",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid call insn1",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "BPF_CALL uses reserved",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid call insn2",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 1, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "BPF_CALL uses reserved",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid function call",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid func 1234567",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"uninitialized stack1",
|
||||
.insns = {
|
||||
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_unspec),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup = {2},
|
||||
.errstr = "invalid indirect read from stack",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"uninitialized stack2",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid read from stack",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"check valid spill/fill",
|
||||
.insns = {
|
||||
/* spill R1(ctx) into stack */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
|
||||
|
||||
/* fill it back into R2 */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
|
||||
|
||||
/* should be able to access R0 = *(R2 + 8) */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"check corrupted spill/fill",
|
||||
.insns = {
|
||||
/* spill R1(ctx) into stack */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
|
||||
|
||||
/* mess up with R1 pointer on stack */
|
||||
BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23),
|
||||
|
||||
/* fill back into R0 should fail */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
|
||||
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "corrupted spill",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid src register in STX",
|
||||
.insns = {
|
||||
BPF_STX_MEM(BPF_B, BPF_REG_10, -1, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R15 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid dst register in STX",
|
||||
.insns = {
|
||||
BPF_STX_MEM(BPF_B, 14, BPF_REG_10, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R14 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid dst register in ST",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_B, 14, -1, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R14 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid src register in LDX",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, 12, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R12 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid dst register in LDX",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_B, 11, BPF_REG_1, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R11 is invalid",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"junk insn",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(0, 0, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid BPF_LD_IMM",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"junk insn2",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(1, 0, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "BPF_LDX uses reserved fields",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"junk insn3",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(-1, 0, 0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid BPF_ALU opcode f0",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"junk insn4",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(-1, -1, -1, -1, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid BPF_ALU opcode f0",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"junk insn5",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(0x7f, -1, -1, -1, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "BPF_ALU uses reserved fields",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"misaligned read from stack",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "misaligned access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"invalid map_fd for function call",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_ALU64_REG(BPF_MOV, 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_unspec),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "fd 0 is not pointing to valid bpf_map",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"don't check return value before access",
|
||||
.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_unspec),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup = {3},
|
||||
.errstr = "R0 invalid mem access 'map_value_or_null'",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"access memory with incorrect alignment",
|
||||
.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_unspec),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup = {3},
|
||||
.errstr = "misaligned access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"sometimes access memory with incorrect alignment",
|
||||
.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_unspec),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup = {3},
|
||||
.errstr = "R0 invalid mem access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"jump test 1",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -8),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 5),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"jump test 2",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 14),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 11),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 8),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 5),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"jump test 3",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 19),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 3),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 15),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 3),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 11),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 3),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -40),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 7),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 3),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -48),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 0),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -56),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup = {24},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"jump test 4",
|
||||
.insns = {
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
};
|
||||
|
||||
static int probe_filter_length(struct bpf_insn *fp)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
for (len = MAX_INSNS - 1; len > 0; --len)
|
||||
if (fp[len].code != 0 || fp[len].imm != 0)
|
||||
break;
|
||||
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
static int create_map(void)
|
||||
{
|
||||
long long key, value = 0;
|
||||
int map_fd;
|
||||
|
||||
map_fd = bpf_create_map(BPF_MAP_TYPE_UNSPEC, sizeof(key), sizeof(value), 1024);
|
||||
if (map_fd < 0) {
|
||||
printf("failed to create map '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
return map_fd;
|
||||
}
|
||||
|
||||
static int test(void)
|
||||
{
|
||||
int prog_fd, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
struct bpf_insn *prog = tests[i].insns;
|
||||
int prog_len = probe_filter_length(prog);
|
||||
int *fixup = tests[i].fixup;
|
||||
int map_fd = -1;
|
||||
|
||||
if (*fixup) {
|
||||
map_fd = create_map();
|
||||
|
||||
do {
|
||||
prog[*fixup].imm = map_fd;
|
||||
fixup++;
|
||||
} while (*fixup);
|
||||
}
|
||||
printf("#%d %s ", i, tests[i].descr);
|
||||
|
||||
prog_fd = bpf_prog_load(BPF_PROG_TYPE_UNSPEC, prog,
|
||||
prog_len * sizeof(struct bpf_insn),
|
||||
"GPL");
|
||||
|
||||
if (tests[i].result == ACCEPT) {
|
||||
if (prog_fd < 0) {
|
||||
printf("FAIL\nfailed to load prog '%s'\n",
|
||||
strerror(errno));
|
||||
printf("%s", bpf_log_buf);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if (prog_fd >= 0) {
|
||||
printf("FAIL\nunexpected success to load\n");
|
||||
printf("%s", bpf_log_buf);
|
||||
goto fail;
|
||||
}
|
||||
if (strstr(bpf_log_buf, tests[i].errstr) == 0) {
|
||||
printf("FAIL\nunexpected error message: %s",
|
||||
bpf_log_buf);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
fail:
|
||||
if (map_fd >= 0)
|
||||
close(map_fd);
|
||||
close(prog_fd);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test();
|
||||
}
|
10
samples/hidraw/Makefile
Normal file
10
samples/hidraw/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
||||
obj- := dummy.o
|
||||
|
||||
# List of programs to build
|
||||
hostprogs-y := hid-example
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
always := $(hostprogs-y)
|
||||
|
||||
HOSTCFLAGS_hid-example.o += -I$(objtree)/usr/include
|
177
samples/hidraw/hid-example.c
Normal file
177
samples/hidraw/hid-example.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Hidraw Userspace Example
|
||||
*
|
||||
* Copyright (c) 2010 Alan Ott <alan@signal11.us>
|
||||
* Copyright (c) 2010 Signal 11 Software
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using hidraw.
|
||||
*/
|
||||
|
||||
/* Linux */
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hidraw.h>
|
||||
|
||||
/*
|
||||
* Ugly hack to work around failing compilation on systems that don't
|
||||
* yet populate new version of hidraw.h to userspace.
|
||||
*/
|
||||
#ifndef HIDIOCSFEATURE
|
||||
#warning Please have your distro update the userspace kernel headers
|
||||
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
|
||||
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
|
||||
#endif
|
||||
|
||||
/* Unix */
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* C */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char *bus_str(int bus);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
int i, res, desc_size = 0;
|
||||
char buf[256];
|
||||
struct hidraw_report_descriptor rpt_desc;
|
||||
struct hidraw_devinfo info;
|
||||
|
||||
/* Open the Device with non-blocking reads. In real life,
|
||||
don't use a hard coded path; use libudev instead. */
|
||||
fd = open("/dev/hidraw0", O_RDWR|O_NONBLOCK);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("Unable to open device");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&rpt_desc, 0x0, sizeof(rpt_desc));
|
||||
memset(&info, 0x0, sizeof(info));
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
|
||||
/* Get Report Descriptor Size */
|
||||
res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);
|
||||
if (res < 0)
|
||||
perror("HIDIOCGRDESCSIZE");
|
||||
else
|
||||
printf("Report Descriptor Size: %d\n", desc_size);
|
||||
|
||||
/* Get Report Descriptor */
|
||||
rpt_desc.size = desc_size;
|
||||
res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);
|
||||
if (res < 0) {
|
||||
perror("HIDIOCGRDESC");
|
||||
} else {
|
||||
printf("Report Descriptor:\n");
|
||||
for (i = 0; i < rpt_desc.size; i++)
|
||||
printf("%hhx ", rpt_desc.value[i]);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* Get Raw Name */
|
||||
res = ioctl(fd, HIDIOCGRAWNAME(256), buf);
|
||||
if (res < 0)
|
||||
perror("HIDIOCGRAWNAME");
|
||||
else
|
||||
printf("Raw Name: %s\n", buf);
|
||||
|
||||
/* Get Physical Location */
|
||||
res = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
|
||||
if (res < 0)
|
||||
perror("HIDIOCGRAWPHYS");
|
||||
else
|
||||
printf("Raw Phys: %s\n", buf);
|
||||
|
||||
/* Get Raw Info */
|
||||
res = ioctl(fd, HIDIOCGRAWINFO, &info);
|
||||
if (res < 0) {
|
||||
perror("HIDIOCGRAWINFO");
|
||||
} else {
|
||||
printf("Raw Info:\n");
|
||||
printf("\tbustype: %d (%s)\n",
|
||||
info.bustype, bus_str(info.bustype));
|
||||
printf("\tvendor: 0x%04hx\n", info.vendor);
|
||||
printf("\tproduct: 0x%04hx\n", info.product);
|
||||
}
|
||||
|
||||
/* Set Feature */
|
||||
buf[0] = 0x9; /* Report Number */
|
||||
buf[1] = 0xff;
|
||||
buf[2] = 0xff;
|
||||
buf[3] = 0xff;
|
||||
res = ioctl(fd, HIDIOCSFEATURE(4), buf);
|
||||
if (res < 0)
|
||||
perror("HIDIOCSFEATURE");
|
||||
else
|
||||
printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
|
||||
|
||||
/* Get Feature */
|
||||
buf[0] = 0x9; /* Report Number */
|
||||
res = ioctl(fd, HIDIOCGFEATURE(256), buf);
|
||||
if (res < 0) {
|
||||
perror("HIDIOCGFEATURE");
|
||||
} else {
|
||||
printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
|
||||
printf("Report data (not containing the report number):\n\t");
|
||||
for (i = 0; i < res; i++)
|
||||
printf("%hhx ", buf[i]);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* Send a Report to the Device */
|
||||
buf[0] = 0x1; /* Report Number */
|
||||
buf[1] = 0x77;
|
||||
res = write(fd, buf, 2);
|
||||
if (res < 0) {
|
||||
printf("Error: %d\n", errno);
|
||||
perror("write");
|
||||
} else {
|
||||
printf("write() wrote %d bytes\n", res);
|
||||
}
|
||||
|
||||
/* Get a report from the device */
|
||||
res = read(fd, buf, 16);
|
||||
if (res < 0) {
|
||||
perror("read");
|
||||
} else {
|
||||
printf("read() read %d bytes:\n\t", res);
|
||||
for (i = 0; i < res; i++)
|
||||
printf("%hhx ", buf[i]);
|
||||
puts("\n");
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *
|
||||
bus_str(int bus)
|
||||
{
|
||||
switch (bus) {
|
||||
case BUS_USB:
|
||||
return "USB";
|
||||
break;
|
||||
case BUS_HIL:
|
||||
return "HIL";
|
||||
break;
|
||||
case BUS_BLUETOOTH:
|
||||
return "Bluetooth";
|
||||
break;
|
||||
case BUS_VIRTUAL:
|
||||
return "Virtual";
|
||||
break;
|
||||
default:
|
||||
return "Other";
|
||||
break;
|
||||
}
|
||||
}
|
1
samples/hw_breakpoint/Makefile
Normal file
1
samples/hw_breakpoint/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += data_breakpoint.o
|
90
samples/hw_breakpoint/data_breakpoint.c
Normal file
90
samples/hw_breakpoint/data_breakpoint.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* data_breakpoint.c - Sample HW Breakpoint file to watch kernel data address
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* usage: insmod data_breakpoint.ko ksym=<ksym_name>
|
||||
*
|
||||
* This file is a kernel module that places a breakpoint over ksym_name kernel
|
||||
* variable using Hardware Breakpoint register. The corresponding handler which
|
||||
* prints a backtrace is invoked every time a write operation is performed on
|
||||
* that variable.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2009
|
||||
*
|
||||
* Author: K.Prasad <prasad@linux.vnet.ibm.com>
|
||||
*/
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
#include <linux/init.h> /* Needed for the macros */
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
struct perf_event * __percpu *sample_hbp;
|
||||
|
||||
static char ksym_name[KSYM_NAME_LEN] = "pid_max";
|
||||
module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO);
|
||||
MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
|
||||
" write operations on the kernel symbol");
|
||||
|
||||
static void sample_hbp_handler(struct perf_event *bp,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
printk(KERN_INFO "%s value is changed\n", ksym_name);
|
||||
dump_stack();
|
||||
printk(KERN_INFO "Dump stack from sample_hbp_handler\n");
|
||||
}
|
||||
|
||||
static int __init hw_break_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct perf_event_attr attr;
|
||||
|
||||
hw_breakpoint_init(&attr);
|
||||
attr.bp_addr = kallsyms_lookup_name(ksym_name);
|
||||
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
||||
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
|
||||
|
||||
sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
|
||||
if (IS_ERR((void __force *)sample_hbp)) {
|
||||
ret = PTR_ERR((void __force *)sample_hbp);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "HW Breakpoint for %s write installed\n", ksym_name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printk(KERN_INFO "Breakpoint registration failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit hw_break_module_exit(void)
|
||||
{
|
||||
unregister_wide_hw_breakpoint(sample_hbp);
|
||||
printk(KERN_INFO "HW Breakpoint for %s write uninstalled\n", ksym_name);
|
||||
}
|
||||
|
||||
module_init(hw_break_module_init);
|
||||
module_exit(hw_break_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("K.Prasad");
|
||||
MODULE_DESCRIPTION("ksym breakpoint");
|
1
samples/kdb/Makefile
Normal file
1
samples/kdb/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_SAMPLE_KDB) += kdb_hello.o
|
60
samples/kdb/kdb_hello.c
Normal file
60
samples/kdb/kdb_hello.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Created by: Jason Wessel <jason.wessel@windriver.com>
|
||||
*
|
||||
* Copyright (c) 2010 Wind River Systems, Inc. All Rights Reserved.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kdb.h>
|
||||
|
||||
/*
|
||||
* All kdb shell command call backs receive argc and argv, where
|
||||
* argv[0] is the command the end user typed
|
||||
*/
|
||||
static int kdb_hello_cmd(int argc, const char **argv)
|
||||
{
|
||||
if (argc > 1)
|
||||
return KDB_ARGCOUNT;
|
||||
|
||||
if (argc)
|
||||
kdb_printf("Hello %s.\n", argv[1]);
|
||||
else
|
||||
kdb_printf("Hello world!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init kdb_hello_cmd_init(void)
|
||||
{
|
||||
/*
|
||||
* Registration of a dynamically added kdb command is done with
|
||||
* kdb_register() with the arguments being:
|
||||
* 1: The name of the shell command
|
||||
* 2: The function that processes the command
|
||||
* 3: Description of the usage of any arguments
|
||||
* 4: Descriptive text when you run help
|
||||
* 5: Number of characters to complete the command
|
||||
* 0 == type the whole command
|
||||
* 1 == match both "g" and "go" for example
|
||||
*/
|
||||
kdb_register("hello", kdb_hello_cmd, "[string]",
|
||||
"Say Hello World or Hello [string]", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit kdb_hello_cmd_exit(void)
|
||||
{
|
||||
kdb_unregister("hello");
|
||||
}
|
||||
|
||||
module_init(kdb_hello_cmd_init);
|
||||
module_exit(kdb_hello_cmd_exit);
|
||||
|
||||
MODULE_AUTHOR("WindRiver");
|
||||
MODULE_DESCRIPTION("KDB example to add a hello command");
|
||||
MODULE_LICENSE("GPL");
|
1
samples/kfifo/Makefile
Normal file
1
samples/kfifo/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_SAMPLE_KFIFO) += bytestream-example.o dma-example.o inttype-example.o record-example.o
|
194
samples/kfifo/bytestream-example.c
Normal file
194
samples/kfifo/bytestream-example.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Sample kfifo byte stream implementation
|
||||
*
|
||||
* Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
/*
|
||||
* This module shows how to create a byte stream fifo.
|
||||
*/
|
||||
|
||||
/* fifo size in elements (bytes) */
|
||||
#define FIFO_SIZE 32
|
||||
|
||||
/* name of the proc entry */
|
||||
#define PROC_FIFO "bytestream-fifo"
|
||||
|
||||
/* lock for procfs read access */
|
||||
static DEFINE_MUTEX(read_lock);
|
||||
|
||||
/* lock for procfs write access */
|
||||
static DEFINE_MUTEX(write_lock);
|
||||
|
||||
/*
|
||||
* define DYNAMIC in this example for a dynamically allocated fifo.
|
||||
*
|
||||
* Otherwise the fifo storage will be a part of the fifo structure.
|
||||
*/
|
||||
#if 0
|
||||
#define DYNAMIC
|
||||
#endif
|
||||
|
||||
#ifdef DYNAMIC
|
||||
static struct kfifo test;
|
||||
#else
|
||||
static DECLARE_KFIFO(test, unsigned char, FIFO_SIZE);
|
||||
#endif
|
||||
|
||||
static const unsigned char expected_result[FIFO_SIZE] = {
|
||||
3, 4, 5, 6, 7, 8, 9, 0,
|
||||
1, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34,
|
||||
35, 36, 37, 38, 39, 40, 41, 42,
|
||||
};
|
||||
|
||||
static int __init testfunc(void)
|
||||
{
|
||||
unsigned char buf[6];
|
||||
unsigned char i, j;
|
||||
unsigned int ret;
|
||||
|
||||
printk(KERN_INFO "byte stream fifo test start\n");
|
||||
|
||||
/* put string into the fifo */
|
||||
kfifo_in(&test, "hello", 5);
|
||||
|
||||
/* put values into the fifo */
|
||||
for (i = 0; i != 10; i++)
|
||||
kfifo_put(&test, i);
|
||||
|
||||
/* show the number of used elements */
|
||||
printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
|
||||
|
||||
/* get max of 5 bytes from the fifo */
|
||||
i = kfifo_out(&test, buf, 5);
|
||||
printk(KERN_INFO "buf: %.*s\n", i, buf);
|
||||
|
||||
/* get max of 2 elements from the fifo */
|
||||
ret = kfifo_out(&test, buf, 2);
|
||||
printk(KERN_INFO "ret: %d\n", ret);
|
||||
/* and put it back to the end of the fifo */
|
||||
ret = kfifo_in(&test, buf, ret);
|
||||
printk(KERN_INFO "ret: %d\n", ret);
|
||||
|
||||
/* skip first element of the fifo */
|
||||
printk(KERN_INFO "skip 1st element\n");
|
||||
kfifo_skip(&test);
|
||||
|
||||
/* put values into the fifo until is full */
|
||||
for (i = 20; kfifo_put(&test, i); i++)
|
||||
;
|
||||
|
||||
printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
|
||||
|
||||
/* show the first value without removing from the fifo */
|
||||
if (kfifo_peek(&test, &i))
|
||||
printk(KERN_INFO "%d\n", i);
|
||||
|
||||
/* check the correctness of all values in the fifo */
|
||||
j = 0;
|
||||
while (kfifo_get(&test, &i)) {
|
||||
printk(KERN_INFO "item = %d\n", i);
|
||||
if (i != expected_result[j++]) {
|
||||
printk(KERN_WARNING "value mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (j != ARRAY_SIZE(expected_result)) {
|
||||
printk(KERN_WARNING "size mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_INFO "test passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fifo_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&write_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_from_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&write_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static ssize_t fifo_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&read_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_to_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&read_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static const struct file_operations fifo_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = fifo_read,
|
||||
.write = fifo_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
#ifdef DYNAMIC
|
||||
int ret;
|
||||
|
||||
ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "error kfifo_alloc\n");
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
INIT_KFIFO(test);
|
||||
#endif
|
||||
if (testfunc() < 0) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
remove_proc_entry(PROC_FIFO, NULL);
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
|
143
samples/kfifo/dma-example.c
Normal file
143
samples/kfifo/dma-example.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Sample fifo dma implementation
|
||||
*
|
||||
* Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
/*
|
||||
* This module shows how to handle fifo dma operations.
|
||||
*/
|
||||
|
||||
/* fifo size in elements (bytes) */
|
||||
#define FIFO_SIZE 32
|
||||
|
||||
static struct kfifo fifo;
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
int i;
|
||||
unsigned int ret;
|
||||
unsigned int nents;
|
||||
struct scatterlist sg[10];
|
||||
|
||||
printk(KERN_INFO "DMA fifo test start\n");
|
||||
|
||||
if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
|
||||
printk(KERN_WARNING "error kfifo_alloc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "queue size: %u\n", kfifo_size(&fifo));
|
||||
|
||||
kfifo_in(&fifo, "test", 4);
|
||||
|
||||
for (i = 0; i != 9; i++)
|
||||
kfifo_put(&fifo, i);
|
||||
|
||||
/* kick away first byte */
|
||||
kfifo_skip(&fifo);
|
||||
|
||||
printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
|
||||
|
||||
/*
|
||||
* Configure the kfifo buffer to receive data from DMA input.
|
||||
*
|
||||
* .--------------------------------------.
|
||||
* | 0 | 1 | 2 | ... | 12 | 13 | ... | 31 |
|
||||
* |---|------------------|---------------|
|
||||
* \_/ \________________/ \_____________/
|
||||
* \ \ \
|
||||
* \ \_allocated data \
|
||||
* \_*free space* \_*free space*
|
||||
*
|
||||
* We need two different SG entries: one for the free space area at the
|
||||
* end of the kfifo buffer (19 bytes) and another for the first free
|
||||
* byte at the beginning, after the kfifo_skip().
|
||||
*/
|
||||
sg_init_table(sg, ARRAY_SIZE(sg));
|
||||
nents = kfifo_dma_in_prepare(&fifo, sg, ARRAY_SIZE(sg), FIFO_SIZE);
|
||||
printk(KERN_INFO "DMA sgl entries: %d\n", nents);
|
||||
if (!nents) {
|
||||
/* fifo is full and no sgl was created */
|
||||
printk(KERN_WARNING "error kfifo_dma_in_prepare\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* receive data */
|
||||
printk(KERN_INFO "scatterlist for receive:\n");
|
||||
for (i = 0; i < nents; i++) {
|
||||
printk(KERN_INFO
|
||||
"sg[%d] -> "
|
||||
"page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
|
||||
i, sg[i].page_link, sg[i].offset, sg[i].length);
|
||||
|
||||
if (sg_is_last(&sg[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
/* put here your code to setup and exectute the dma operation */
|
||||
/* ... */
|
||||
|
||||
/* example: zero bytes received */
|
||||
ret = 0;
|
||||
|
||||
/* finish the dma operation and update the received data */
|
||||
kfifo_dma_in_finish(&fifo, ret);
|
||||
|
||||
/* Prepare to transmit data, example: 8 bytes */
|
||||
nents = kfifo_dma_out_prepare(&fifo, sg, ARRAY_SIZE(sg), 8);
|
||||
printk(KERN_INFO "DMA sgl entries: %d\n", nents);
|
||||
if (!nents) {
|
||||
/* no data was available and no sgl was created */
|
||||
printk(KERN_WARNING "error kfifo_dma_out_prepare\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "scatterlist for transmit:\n");
|
||||
for (i = 0; i < nents; i++) {
|
||||
printk(KERN_INFO
|
||||
"sg[%d] -> "
|
||||
"page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
|
||||
i, sg[i].page_link, sg[i].offset, sg[i].length);
|
||||
|
||||
if (sg_is_last(&sg[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
/* put here your code to setup and exectute the dma operation */
|
||||
/* ... */
|
||||
|
||||
/* example: 5 bytes transmitted */
|
||||
ret = 5;
|
||||
|
||||
/* finish the dma operation and update the transmitted data */
|
||||
kfifo_dma_out_finish(&fifo, ret);
|
||||
|
||||
ret = kfifo_len(&fifo);
|
||||
printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
|
||||
|
||||
if (ret != 7) {
|
||||
printk(KERN_WARNING "size mismatch: test failed");
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_INFO "test passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
kfifo_free(&fifo);
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
|
185
samples/kfifo/inttype-example.c
Normal file
185
samples/kfifo/inttype-example.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Sample kfifo int type implementation
|
||||
*
|
||||
* Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
/*
|
||||
* This module shows how to create a int type fifo.
|
||||
*/
|
||||
|
||||
/* fifo size in elements (ints) */
|
||||
#define FIFO_SIZE 32
|
||||
|
||||
/* name of the proc entry */
|
||||
#define PROC_FIFO "int-fifo"
|
||||
|
||||
/* lock for procfs read access */
|
||||
static DEFINE_MUTEX(read_lock);
|
||||
|
||||
/* lock for procfs write access */
|
||||
static DEFINE_MUTEX(write_lock);
|
||||
|
||||
/*
|
||||
* define DYNAMIC in this example for a dynamically allocated fifo.
|
||||
*
|
||||
* Otherwise the fifo storage will be a part of the fifo structure.
|
||||
*/
|
||||
#if 0
|
||||
#define DYNAMIC
|
||||
#endif
|
||||
|
||||
#ifdef DYNAMIC
|
||||
static DECLARE_KFIFO_PTR(test, int);
|
||||
#else
|
||||
static DEFINE_KFIFO(test, int, FIFO_SIZE);
|
||||
#endif
|
||||
|
||||
static const int expected_result[FIFO_SIZE] = {
|
||||
3, 4, 5, 6, 7, 8, 9, 0,
|
||||
1, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34,
|
||||
35, 36, 37, 38, 39, 40, 41, 42,
|
||||
};
|
||||
|
||||
static int __init testfunc(void)
|
||||
{
|
||||
int buf[6];
|
||||
int i, j;
|
||||
unsigned int ret;
|
||||
|
||||
printk(KERN_INFO "int fifo test start\n");
|
||||
|
||||
/* put values into the fifo */
|
||||
for (i = 0; i != 10; i++)
|
||||
kfifo_put(&test, i);
|
||||
|
||||
/* show the number of used elements */
|
||||
printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
|
||||
|
||||
/* get max of 2 elements from the fifo */
|
||||
ret = kfifo_out(&test, buf, 2);
|
||||
printk(KERN_INFO "ret: %d\n", ret);
|
||||
/* and put it back to the end of the fifo */
|
||||
ret = kfifo_in(&test, buf, ret);
|
||||
printk(KERN_INFO "ret: %d\n", ret);
|
||||
|
||||
/* skip first element of the fifo */
|
||||
printk(KERN_INFO "skip 1st element\n");
|
||||
kfifo_skip(&test);
|
||||
|
||||
/* put values into the fifo until is full */
|
||||
for (i = 20; kfifo_put(&test, i); i++)
|
||||
;
|
||||
|
||||
printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
|
||||
|
||||
/* show the first value without removing from the fifo */
|
||||
if (kfifo_peek(&test, &i))
|
||||
printk(KERN_INFO "%d\n", i);
|
||||
|
||||
/* check the correctness of all values in the fifo */
|
||||
j = 0;
|
||||
while (kfifo_get(&test, &i)) {
|
||||
printk(KERN_INFO "item = %d\n", i);
|
||||
if (i != expected_result[j++]) {
|
||||
printk(KERN_WARNING "value mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (j != ARRAY_SIZE(expected_result)) {
|
||||
printk(KERN_WARNING "size mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_INFO "test passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fifo_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&write_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_from_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&write_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static ssize_t fifo_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&read_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_to_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&read_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static const struct file_operations fifo_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = fifo_read,
|
||||
.write = fifo_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
#ifdef DYNAMIC
|
||||
int ret;
|
||||
|
||||
ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "error kfifo_alloc\n");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (testfunc() < 0) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
remove_proc_entry(PROC_FIFO, NULL);
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
|
201
samples/kfifo/record-example.c
Normal file
201
samples/kfifo/record-example.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Sample dynamic sized record fifo implementation
|
||||
*
|
||||
* Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
/*
|
||||
* This module shows how to create a variable sized record fifo.
|
||||
*/
|
||||
|
||||
/* fifo size in elements (bytes) */
|
||||
#define FIFO_SIZE 128
|
||||
|
||||
/* name of the proc entry */
|
||||
#define PROC_FIFO "record-fifo"
|
||||
|
||||
/* lock for procfs read access */
|
||||
static DEFINE_MUTEX(read_lock);
|
||||
|
||||
/* lock for procfs write access */
|
||||
static DEFINE_MUTEX(write_lock);
|
||||
|
||||
/*
|
||||
* define DYNAMIC in this example for a dynamically allocated fifo.
|
||||
*
|
||||
* Otherwise the fifo storage will be a part of the fifo structure.
|
||||
*/
|
||||
#if 0
|
||||
#define DYNAMIC
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct kfifo_rec_ptr_1 and STRUCT_KFIFO_REC_1 can handle records of a
|
||||
* length between 0 and 255 bytes.
|
||||
*
|
||||
* struct kfifo_rec_ptr_2 and STRUCT_KFIFO_REC_2 can handle records of a
|
||||
* length between 0 and 65535 bytes.
|
||||
*/
|
||||
|
||||
#ifdef DYNAMIC
|
||||
struct kfifo_rec_ptr_1 test;
|
||||
|
||||
#else
|
||||
typedef STRUCT_KFIFO_REC_1(FIFO_SIZE) mytest;
|
||||
|
||||
static mytest test;
|
||||
#endif
|
||||
|
||||
static const char *expected_result[] = {
|
||||
"a",
|
||||
"bb",
|
||||
"ccc",
|
||||
"dddd",
|
||||
"eeeee",
|
||||
"ffffff",
|
||||
"ggggggg",
|
||||
"hhhhhhhh",
|
||||
"iiiiiiiii",
|
||||
"jjjjjjjjjj",
|
||||
};
|
||||
|
||||
static int __init testfunc(void)
|
||||
{
|
||||
char buf[100];
|
||||
unsigned int i;
|
||||
unsigned int ret;
|
||||
struct { unsigned char buf[6]; } hello = { "hello" };
|
||||
|
||||
printk(KERN_INFO "record fifo test start\n");
|
||||
|
||||
kfifo_in(&test, &hello, sizeof(hello));
|
||||
|
||||
/* show the size of the next record in the fifo */
|
||||
printk(KERN_INFO "fifo peek len: %u\n", kfifo_peek_len(&test));
|
||||
|
||||
/* put in variable length data */
|
||||
for (i = 0; i < 10; i++) {
|
||||
memset(buf, 'a' + i, i + 1);
|
||||
kfifo_in(&test, buf, i + 1);
|
||||
}
|
||||
|
||||
/* skip first element of the fifo */
|
||||
printk(KERN_INFO "skip 1st element\n");
|
||||
kfifo_skip(&test);
|
||||
|
||||
printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
|
||||
|
||||
/* show the first record without removing from the fifo */
|
||||
ret = kfifo_out_peek(&test, buf, sizeof(buf));
|
||||
if (ret)
|
||||
printk(KERN_INFO "%.*s\n", ret, buf);
|
||||
|
||||
/* check the correctness of all values in the fifo */
|
||||
i = 0;
|
||||
while (!kfifo_is_empty(&test)) {
|
||||
ret = kfifo_out(&test, buf, sizeof(buf));
|
||||
buf[ret] = '\0';
|
||||
printk(KERN_INFO "item = %.*s\n", ret, buf);
|
||||
if (strcmp(buf, expected_result[i++])) {
|
||||
printk(KERN_WARNING "value mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (i != ARRAY_SIZE(expected_result)) {
|
||||
printk(KERN_WARNING "size mismatch: test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_INFO "test passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fifo_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&write_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_from_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&write_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static ssize_t fifo_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
unsigned int copied;
|
||||
|
||||
if (mutex_lock_interruptible(&read_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_to_user(&test, buf, count, &copied);
|
||||
|
||||
mutex_unlock(&read_lock);
|
||||
|
||||
return ret ? ret : copied;
|
||||
}
|
||||
|
||||
static const struct file_operations fifo_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = fifo_read,
|
||||
.write = fifo_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
#ifdef DYNAMIC
|
||||
int ret;
|
||||
|
||||
ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "error kfifo_alloc\n");
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
INIT_KFIFO(test);
|
||||
#endif
|
||||
if (testfunc() < 0) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
remove_proc_entry(PROC_FIFO, NULL);
|
||||
#ifdef DYNAMIC
|
||||
kfifo_free(&test);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
|
1
samples/kobject/Makefile
Normal file
1
samples/kobject/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_SAMPLE_KOBJECT) += kobject-example.o kset-example.o
|
138
samples/kobject/kobject-example.c
Normal file
138
samples/kobject/kobject-example.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Sample kobject implementation
|
||||
*
|
||||
* Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* Copyright (C) 2007 Novell Inc.
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/*
|
||||
* This module shows how to create a simple subdirectory in sysfs called
|
||||
* /sys/kernel/kobject-example In that directory, 3 files are created:
|
||||
* "foo", "baz", and "bar". If an integer is written to these files, it can be
|
||||
* later read out of it.
|
||||
*/
|
||||
|
||||
static int foo;
|
||||
static int baz;
|
||||
static int bar;
|
||||
|
||||
/*
|
||||
* The "foo" file where a static variable is read from and written to.
|
||||
*/
|
||||
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", foo);
|
||||
}
|
||||
|
||||
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%du", &foo);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Sysfs attributes cannot be world-writable. */
|
||||
static struct kobj_attribute foo_attribute =
|
||||
__ATTR(foo, 0664, foo_show, foo_store);
|
||||
|
||||
/*
|
||||
* More complex function where we determine which variable is being accessed by
|
||||
* looking at the attribute for the "baz" and "bar" files.
|
||||
*/
|
||||
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int var;
|
||||
|
||||
if (strcmp(attr->attr.name, "baz") == 0)
|
||||
var = baz;
|
||||
else
|
||||
var = bar;
|
||||
return sprintf(buf, "%d\n", var);
|
||||
}
|
||||
|
||||
static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int var;
|
||||
|
||||
sscanf(buf, "%du", &var);
|
||||
if (strcmp(attr->attr.name, "baz") == 0)
|
||||
baz = var;
|
||||
else
|
||||
bar = var;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute baz_attribute =
|
||||
__ATTR(baz, 0664, b_show, b_store);
|
||||
static struct kobj_attribute bar_attribute =
|
||||
__ATTR(bar, 0664, b_show, b_store);
|
||||
|
||||
|
||||
/*
|
||||
* Create a group of attributes so that we can create and destroy them all
|
||||
* at once.
|
||||
*/
|
||||
static struct attribute *attrs[] = {
|
||||
&foo_attribute.attr,
|
||||
&baz_attribute.attr,
|
||||
&bar_attribute.attr,
|
||||
NULL, /* need to NULL terminate the list of attributes */
|
||||
};
|
||||
|
||||
/*
|
||||
* An unnamed attribute group will put all of the attributes directly in
|
||||
* the kobject directory. If we specify a name, a subdirectory will be
|
||||
* created for the attributes with the directory being the name of the
|
||||
* attribute group.
|
||||
*/
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
static struct kobject *example_kobj;
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Create a simple kobject with the name of "kobject_example",
|
||||
* located under /sys/kernel/
|
||||
*
|
||||
* As this is a simple directory, no uevent will be sent to
|
||||
* userspace. That is why this function should not be used for
|
||||
* any type of dynamic kobjects, where the name and number are
|
||||
* not known ahead of time.
|
||||
*/
|
||||
example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
|
||||
if (!example_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create the files associated with this kobject */
|
||||
retval = sysfs_create_group(example_kobj, &attr_group);
|
||||
if (retval)
|
||||
kobject_put(example_kobj);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
kobject_put(example_kobj);
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
|
281
samples/kobject/kset-example.c
Normal file
281
samples/kobject/kset-example.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Sample kset and ktype implementation
|
||||
*
|
||||
* Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
|
||||
* Copyright (C) 2007 Novell Inc.
|
||||
*
|
||||
* Released under the GPL version 2 only.
|
||||
*
|
||||
*/
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/*
|
||||
* This module shows how to create a kset in sysfs called
|
||||
* /sys/kernel/kset-example
|
||||
* Then tree kobjects are created and assigned to this kset, "foo", "baz",
|
||||
* and "bar". In those kobjects, attributes of the same name are also
|
||||
* created and if an integer is written to these files, it can be later
|
||||
* read out of it.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* This is our "object" that we will create a few of and register them with
|
||||
* sysfs.
|
||||
*/
|
||||
struct foo_obj {
|
||||
struct kobject kobj;
|
||||
int foo;
|
||||
int baz;
|
||||
int bar;
|
||||
};
|
||||
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)
|
||||
|
||||
/* a custom attribute that works just for a struct foo_obj. */
|
||||
struct foo_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
|
||||
ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
|
||||
};
|
||||
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)
|
||||
|
||||
/*
|
||||
* The default show function that must be passed to sysfs. This will be
|
||||
* called by sysfs for whenever a show function is called by the user on a
|
||||
* sysfs file associated with the kobjects we have registered. We need to
|
||||
* transpose back from a "default" kobject to our custom struct foo_obj and
|
||||
* then call the show function for that specific object.
|
||||
*/
|
||||
static ssize_t foo_attr_show(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct foo_attribute *attribute;
|
||||
struct foo_obj *foo;
|
||||
|
||||
attribute = to_foo_attr(attr);
|
||||
foo = to_foo_obj(kobj);
|
||||
|
||||
if (!attribute->show)
|
||||
return -EIO;
|
||||
|
||||
return attribute->show(foo, attribute, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just like the default show function above, but this one is for when the
|
||||
* sysfs "store" is requested (when a value is written to a file.)
|
||||
*/
|
||||
static ssize_t foo_attr_store(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct foo_attribute *attribute;
|
||||
struct foo_obj *foo;
|
||||
|
||||
attribute = to_foo_attr(attr);
|
||||
foo = to_foo_obj(kobj);
|
||||
|
||||
if (!attribute->store)
|
||||
return -EIO;
|
||||
|
||||
return attribute->store(foo, attribute, buf, len);
|
||||
}
|
||||
|
||||
/* Our custom sysfs_ops that we will associate with our ktype later on */
|
||||
static const struct sysfs_ops foo_sysfs_ops = {
|
||||
.show = foo_attr_show,
|
||||
.store = foo_attr_store,
|
||||
};
|
||||
|
||||
/*
|
||||
* The release function for our object. This is REQUIRED by the kernel to
|
||||
* have. We free the memory held in our object here.
|
||||
*
|
||||
* NEVER try to get away with just a "blank" release function to try to be
|
||||
* smarter than the kernel. Turns out, no one ever is...
|
||||
*/
|
||||
static void foo_release(struct kobject *kobj)
|
||||
{
|
||||
struct foo_obj *foo;
|
||||
|
||||
foo = to_foo_obj(kobj);
|
||||
kfree(foo);
|
||||
}
|
||||
|
||||
/*
|
||||
* The "foo" file where the .foo variable is read from and written to.
|
||||
*/
|
||||
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", foo_obj->foo);
|
||||
}
|
||||
|
||||
static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%du", &foo_obj->foo);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Sysfs attributes cannot be world-writable. */
|
||||
static struct foo_attribute foo_attribute =
|
||||
__ATTR(foo, 0664, foo_show, foo_store);
|
||||
|
||||
/*
|
||||
* More complex function where we determine which variable is being accessed by
|
||||
* looking at the attribute for the "baz" and "bar" files.
|
||||
*/
|
||||
static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int var;
|
||||
|
||||
if (strcmp(attr->attr.name, "baz") == 0)
|
||||
var = foo_obj->baz;
|
||||
else
|
||||
var = foo_obj->bar;
|
||||
return sprintf(buf, "%d\n", var);
|
||||
}
|
||||
|
||||
static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int var;
|
||||
|
||||
sscanf(buf, "%du", &var);
|
||||
if (strcmp(attr->attr.name, "baz") == 0)
|
||||
foo_obj->baz = var;
|
||||
else
|
||||
foo_obj->bar = var;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct foo_attribute baz_attribute =
|
||||
__ATTR(baz, 0664, b_show, b_store);
|
||||
static struct foo_attribute bar_attribute =
|
||||
__ATTR(bar, 0664, b_show, b_store);
|
||||
|
||||
/*
|
||||
* Create a group of attributes so that we can create and destroy them all
|
||||
* at once.
|
||||
*/
|
||||
static struct attribute *foo_default_attrs[] = {
|
||||
&foo_attribute.attr,
|
||||
&baz_attribute.attr,
|
||||
&bar_attribute.attr,
|
||||
NULL, /* need to NULL terminate the list of attributes */
|
||||
};
|
||||
|
||||
/*
|
||||
* Our own ktype for our kobjects. Here we specify our sysfs ops, the
|
||||
* release function, and the set of default attributes we want created
|
||||
* whenever a kobject of this type is registered with the kernel.
|
||||
*/
|
||||
static struct kobj_type foo_ktype = {
|
||||
.sysfs_ops = &foo_sysfs_ops,
|
||||
.release = foo_release,
|
||||
.default_attrs = foo_default_attrs,
|
||||
};
|
||||
|
||||
static struct kset *example_kset;
|
||||
static struct foo_obj *foo_obj;
|
||||
static struct foo_obj *bar_obj;
|
||||
static struct foo_obj *baz_obj;
|
||||
|
||||
static struct foo_obj *create_foo_obj(const char *name)
|
||||
{
|
||||
struct foo_obj *foo;
|
||||
int retval;
|
||||
|
||||
/* allocate the memory for the whole object */
|
||||
foo = kzalloc(sizeof(*foo), GFP_KERNEL);
|
||||
if (!foo)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* As we have a kset for this kobject, we need to set it before calling
|
||||
* the kobject core.
|
||||
*/
|
||||
foo->kobj.kset = example_kset;
|
||||
|
||||
/*
|
||||
* Initialize and add the kobject to the kernel. All the default files
|
||||
* will be created here. As we have already specified a kset for this
|
||||
* kobject, we don't have to set a parent for the kobject, the kobject
|
||||
* will be placed beneath that kset automatically.
|
||||
*/
|
||||
retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
|
||||
if (retval) {
|
||||
kobject_put(&foo->kobj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are always responsible for sending the uevent that the kobject
|
||||
* was added to the system.
|
||||
*/
|
||||
kobject_uevent(&foo->kobj, KOBJ_ADD);
|
||||
|
||||
return foo;
|
||||
}
|
||||
|
||||
static void destroy_foo_obj(struct foo_obj *foo)
|
||||
{
|
||||
kobject_put(&foo->kobj);
|
||||
}
|
||||
|
||||
static int __init example_init(void)
|
||||
{
|
||||
/*
|
||||
* Create a kset with the name of "kset_example",
|
||||
* located under /sys/kernel/
|
||||
*/
|
||||
example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
|
||||
if (!example_kset)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Create three objects and register them with our kset
|
||||
*/
|
||||
foo_obj = create_foo_obj("foo");
|
||||
if (!foo_obj)
|
||||
goto foo_error;
|
||||
|
||||
bar_obj = create_foo_obj("bar");
|
||||
if (!bar_obj)
|
||||
goto bar_error;
|
||||
|
||||
baz_obj = create_foo_obj("baz");
|
||||
if (!baz_obj)
|
||||
goto baz_error;
|
||||
|
||||
return 0;
|
||||
|
||||
baz_error:
|
||||
destroy_foo_obj(bar_obj);
|
||||
bar_error:
|
||||
destroy_foo_obj(foo_obj);
|
||||
foo_error:
|
||||
kset_unregister(example_kset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void __exit example_exit(void)
|
||||
{
|
||||
destroy_foo_obj(baz_obj);
|
||||
destroy_foo_obj(bar_obj);
|
||||
destroy_foo_obj(foo_obj);
|
||||
kset_unregister(example_kset);
|
||||
}
|
||||
|
||||
module_init(example_init);
|
||||
module_exit(example_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
|
5
samples/kprobes/Makefile
Normal file
5
samples/kprobes/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
# builds the kprobes example kernel modules;
|
||||
# then to use one (as root): insmod <module_name.ko>
|
||||
|
||||
obj-$(CONFIG_SAMPLE_KPROBES) += kprobe_example.o jprobe_example.o
|
||||
obj-$(CONFIG_SAMPLE_KRETPROBES) += kretprobe_example.o
|
67
samples/kprobes/jprobe_example.c
Normal file
67
samples/kprobes/jprobe_example.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Here's a sample kernel module showing the use of jprobes to dump
|
||||
* the arguments of do_fork().
|
||||
*
|
||||
* For more information on theory of operation of jprobes, see
|
||||
* Documentation/kprobes.txt
|
||||
*
|
||||
* Build and insert the kernel module as done in the kprobe example.
|
||||
* You will see the trace data in /var/log/messages and on the
|
||||
* console whenever do_fork() is invoked to create a new process.
|
||||
* (Some messages may be suppressed if syslogd is configured to
|
||||
* eliminate duplicate messages.)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
/*
|
||||
* Jumper probe for do_fork.
|
||||
* Mirror principle enables access to arguments of the probed routine
|
||||
* from the probe handler.
|
||||
*/
|
||||
|
||||
/* Proxy routine having the same arguments as actual do_fork() routine */
|
||||
static long jdo_fork(unsigned long clone_flags, unsigned long stack_start,
|
||||
unsigned long stack_size, int __user *parent_tidptr,
|
||||
int __user *child_tidptr)
|
||||
{
|
||||
pr_info("jprobe: clone_flags = 0x%lx, stack_start = 0x%lx "
|
||||
"stack_size = 0x%lx\n", clone_flags, stack_start, stack_size);
|
||||
|
||||
/* Always end with a call to jprobe_return(). */
|
||||
jprobe_return();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jprobe my_jprobe = {
|
||||
.entry = jdo_fork,
|
||||
.kp = {
|
||||
.symbol_name = "do_fork",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init jprobe_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_jprobe(&my_jprobe);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "register_jprobe failed, returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
printk(KERN_INFO "Planted jprobe at %p, handler addr %p\n",
|
||||
my_jprobe.kp.addr, my_jprobe.entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit jprobe_exit(void)
|
||||
{
|
||||
unregister_jprobe(&my_jprobe);
|
||||
printk(KERN_INFO "jprobe at %p unregistered\n", my_jprobe.kp.addr);
|
||||
}
|
||||
|
||||
module_init(jprobe_init)
|
||||
module_exit(jprobe_exit)
|
||||
MODULE_LICENSE("GPL");
|
109
samples/kprobes/kprobe_example.c
Normal file
109
samples/kprobes/kprobe_example.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* NOTE: This example is works on x86 and powerpc.
|
||||
* Here's a sample kernel module showing the use of kprobes to dump a
|
||||
* stack trace and selected registers when do_fork() is called.
|
||||
*
|
||||
* For more information on theory of operation of kprobes, see
|
||||
* Documentation/kprobes.txt
|
||||
*
|
||||
* You will see the trace data in /var/log/messages and on the console
|
||||
* whenever do_fork() is invoked to create a new process.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
/* For each probe you need to allocate a kprobe structure */
|
||||
static struct kprobe kp = {
|
||||
.symbol_name = "do_fork",
|
||||
};
|
||||
|
||||
/* kprobe pre_handler: called just before the probed instruction is executed */
|
||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx,"
|
||||
" flags = 0x%lx\n",
|
||||
p->addr, regs->ip, regs->flags);
|
||||
#endif
|
||||
#ifdef CONFIG_PPC
|
||||
printk(KERN_INFO "pre_handler: p->addr = 0x%p, nip = 0x%lx,"
|
||||
" msr = 0x%lx\n",
|
||||
p->addr, regs->nip, regs->msr);
|
||||
#endif
|
||||
#ifdef CONFIG_MIPS
|
||||
printk(KERN_INFO "pre_handler: p->addr = 0x%p, epc = 0x%lx,"
|
||||
" status = 0x%lx\n",
|
||||
p->addr, regs->cp0_epc, regs->cp0_status);
|
||||
#endif
|
||||
#ifdef CONFIG_TILEGX
|
||||
printk(KERN_INFO "pre_handler: p->addr = 0x%p, pc = 0x%lx,"
|
||||
" ex1 = 0x%lx\n",
|
||||
p->addr, regs->pc, regs->ex1);
|
||||
#endif
|
||||
|
||||
/* A dump_stack() here will give a stack backtrace */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* kprobe post_handler: called after the probed instruction is executed */
|
||||
static void handler_post(struct kprobe *p, struct pt_regs *regs,
|
||||
unsigned long flags)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
printk(KERN_INFO "post_handler: p->addr = 0x%p, flags = 0x%lx\n",
|
||||
p->addr, regs->flags);
|
||||
#endif
|
||||
#ifdef CONFIG_PPC
|
||||
printk(KERN_INFO "post_handler: p->addr = 0x%p, msr = 0x%lx\n",
|
||||
p->addr, regs->msr);
|
||||
#endif
|
||||
#ifdef CONFIG_MIPS
|
||||
printk(KERN_INFO "post_handler: p->addr = 0x%p, status = 0x%lx\n",
|
||||
p->addr, regs->cp0_status);
|
||||
#endif
|
||||
#ifdef CONFIG_TILEGX
|
||||
printk(KERN_INFO "post_handler: p->addr = 0x%p, ex1 = 0x%lx\n",
|
||||
p->addr, regs->ex1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* fault_handler: this is called if an exception is generated for any
|
||||
* instruction within the pre- or post-handler, or when Kprobes
|
||||
* single-steps the probed instruction.
|
||||
*/
|
||||
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
|
||||
{
|
||||
printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%dn",
|
||||
p->addr, trapnr);
|
||||
/* Return 0 because we don't handle the fault. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init kprobe_init(void)
|
||||
{
|
||||
int ret;
|
||||
kp.pre_handler = handler_pre;
|
||||
kp.post_handler = handler_post;
|
||||
kp.fault_handler = handler_fault;
|
||||
|
||||
ret = register_kprobe(&kp);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
printk(KERN_INFO "Planted kprobe at %p\n", kp.addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit kprobe_exit(void)
|
||||
{
|
||||
unregister_kprobe(&kp);
|
||||
printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr);
|
||||
}
|
||||
|
||||
module_init(kprobe_init)
|
||||
module_exit(kprobe_exit)
|
||||
MODULE_LICENSE("GPL");
|
107
samples/kprobes/kretprobe_example.c
Normal file
107
samples/kprobes/kretprobe_example.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* kretprobe_example.c
|
||||
*
|
||||
* Here's a sample kernel module showing the use of return probes to
|
||||
* report the return value and total time taken for probed function
|
||||
* to run.
|
||||
*
|
||||
* usage: insmod kretprobe_example.ko func=<func_name>
|
||||
*
|
||||
* If no func_name is specified, do_fork is instrumented
|
||||
*
|
||||
* For more information on theory of operation of kretprobes, see
|
||||
* Documentation/kprobes.txt
|
||||
*
|
||||
* Build and insert the kernel module as done in the kprobe example.
|
||||
* You will see the trace data in /var/log/messages and on the console
|
||||
* whenever the probed function returns. (Some messages may be suppressed
|
||||
* if syslogd is configured to eliminate duplicate messages.)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
static char func_name[NAME_MAX] = "do_fork";
|
||||
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
|
||||
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
|
||||
" function's execution time");
|
||||
|
||||
/* per-instance private data */
|
||||
struct my_data {
|
||||
ktime_t entry_stamp;
|
||||
};
|
||||
|
||||
/* Here we use the entry_hanlder to timestamp function entry */
|
||||
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct my_data *data;
|
||||
|
||||
if (!current->mm)
|
||||
return 1; /* Skip kernel threads */
|
||||
|
||||
data = (struct my_data *)ri->data;
|
||||
data->entry_stamp = ktime_get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return-probe handler: Log the return value and duration. Duration may turn
|
||||
* out to be zero consistently, depending upon the granularity of time
|
||||
* accounting on the platform.
|
||||
*/
|
||||
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
int retval = regs_return_value(regs);
|
||||
struct my_data *data = (struct my_data *)ri->data;
|
||||
s64 delta;
|
||||
ktime_t now;
|
||||
|
||||
now = ktime_get();
|
||||
delta = ktime_to_ns(ktime_sub(now, data->entry_stamp));
|
||||
printk(KERN_INFO "%s returned %d and took %lld ns to execute\n",
|
||||
func_name, retval, (long long)delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kretprobe my_kretprobe = {
|
||||
.handler = ret_handler,
|
||||
.entry_handler = entry_handler,
|
||||
.data_size = sizeof(struct my_data),
|
||||
/* Probe up to 20 instances concurrently. */
|
||||
.maxactive = 20,
|
||||
};
|
||||
|
||||
static int __init kretprobe_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
my_kretprobe.kp.symbol_name = func_name;
|
||||
ret = register_kretprobe(&my_kretprobe);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "register_kretprobe failed, returned %d\n",
|
||||
ret);
|
||||
return -1;
|
||||
}
|
||||
printk(KERN_INFO "Planted return probe at %s: %p\n",
|
||||
my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit kretprobe_exit(void)
|
||||
{
|
||||
unregister_kretprobe(&my_kretprobe);
|
||||
printk(KERN_INFO "kretprobe at %p unregistered\n",
|
||||
my_kretprobe.kp.addr);
|
||||
|
||||
/* nmissed > 0 suggests that maxactive was set too low. */
|
||||
printk(KERN_INFO "Missed probing %d instances of %s\n",
|
||||
my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
|
||||
}
|
||||
|
||||
module_init(kretprobe_init)
|
||||
module_exit(kretprobe_exit)
|
||||
MODULE_LICENSE("GPL");
|
1
samples/rpmsg/Makefile
Normal file
1
samples/rpmsg/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg_client_sample.o
|
100
samples/rpmsg/rpmsg_client_sample.c
Normal file
100
samples/rpmsg/rpmsg_client_sample.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Remote processor messaging - sample client driver
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* Ohad Ben-Cohen <ohad@wizery.com>
|
||||
* Brian Swetland <swetland@google.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rpmsg.h>
|
||||
|
||||
#define MSG "hello world!"
|
||||
#define MSG_LIMIT 100
|
||||
|
||||
static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len,
|
||||
void *priv, u32 src)
|
||||
{
|
||||
int ret;
|
||||
static int rx_count;
|
||||
|
||||
dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", ++rx_count, src);
|
||||
|
||||
print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
|
||||
data, len, true);
|
||||
|
||||
/* samples should not live forever */
|
||||
if (rx_count >= MSG_LIMIT) {
|
||||
dev_info(&rpdev->dev, "goodbye!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* send a new message now */
|
||||
ret = rpmsg_send(rpdev, MSG, strlen(MSG));
|
||||
if (ret)
|
||||
dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
|
||||
}
|
||||
|
||||
static int rpmsg_sample_probe(struct rpmsg_channel *rpdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
|
||||
rpdev->src, rpdev->dst);
|
||||
|
||||
/* send a message to our remote processor */
|
||||
ret = rpmsg_send(rpdev, MSG, strlen(MSG));
|
||||
if (ret) {
|
||||
dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rpmsg_sample_remove(struct rpmsg_channel *rpdev)
|
||||
{
|
||||
dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n");
|
||||
}
|
||||
|
||||
static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
|
||||
{ .name = "rpmsg-client-sample" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
|
||||
|
||||
static struct rpmsg_driver rpmsg_sample_client = {
|
||||
.drv.name = KBUILD_MODNAME,
|
||||
.drv.owner = THIS_MODULE,
|
||||
.id_table = rpmsg_driver_sample_id_table,
|
||||
.probe = rpmsg_sample_probe,
|
||||
.callback = rpmsg_sample_cb,
|
||||
.remove = rpmsg_sample_remove,
|
||||
};
|
||||
|
||||
static int __init rpmsg_client_sample_init(void)
|
||||
{
|
||||
return register_rpmsg_driver(&rpmsg_sample_client);
|
||||
}
|
||||
module_init(rpmsg_client_sample_init);
|
||||
|
||||
static void __exit rpmsg_client_sample_fini(void)
|
||||
{
|
||||
unregister_rpmsg_driver(&rpmsg_sample_client);
|
||||
}
|
||||
module_exit(rpmsg_client_sample_fini);
|
||||
|
||||
MODULE_DESCRIPTION("Remote processor messaging sample client driver");
|
||||
MODULE_LICENSE("GPL v2");
|
48
samples/seccomp/Makefile
Normal file
48
samples/seccomp/Makefile
Normal file
|
@ -0,0 +1,48 @@
|
|||
# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
||||
obj- := dummy.o
|
||||
|
||||
hostprogs-$(CONFIG_SECCOMP_FILTER) := bpf-fancy dropper bpf-direct
|
||||
|
||||
HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include
|
||||
HOSTCFLAGS_bpf-helper.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_bpf-helper.o += -idirafter $(objtree)/include
|
||||
bpf-fancy-objs := bpf-fancy.o bpf-helper.o
|
||||
|
||||
HOSTCFLAGS_dropper.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_dropper.o += -idirafter $(objtree)/include
|
||||
dropper-objs := dropper.o
|
||||
|
||||
HOSTCFLAGS_bpf-direct.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include
|
||||
bpf-direct-objs := bpf-direct.o
|
||||
|
||||
# Try to match the kernel target.
|
||||
ifndef CROSS_COMPILE
|
||||
ifndef CONFIG_64BIT
|
||||
|
||||
# s390 has -m31 flag to build 31 bit binaries
|
||||
ifndef CONFIG_S390
|
||||
MFLAG = -m32
|
||||
else
|
||||
MFLAG = -m31
|
||||
endif
|
||||
|
||||
HOSTCFLAGS_bpf-direct.o += $(MFLAG)
|
||||
HOSTCFLAGS_dropper.o += $(MFLAG)
|
||||
HOSTCFLAGS_bpf-helper.o += $(MFLAG)
|
||||
HOSTCFLAGS_bpf-fancy.o += $(MFLAG)
|
||||
HOSTLOADLIBES_bpf-direct += $(MFLAG)
|
||||
HOSTLOADLIBES_bpf-fancy += $(MFLAG)
|
||||
HOSTLOADLIBES_dropper += $(MFLAG)
|
||||
endif
|
||||
always := $(hostprogs-y)
|
||||
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)
|
||||
endif
|
||||
endif
|
190
samples/seccomp/bpf-direct.c
Normal file
190
samples/seccomp/bpf-direct.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Seccomp filter example for x86 (32-bit and 64-bit) with BPF macros
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Author: Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using prctl(PR_SET_SECCOMP, 2, ...).
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define SUPPORTED_ARCH 1
|
||||
#endif
|
||||
|
||||
#if defined(SUPPORTED_ARCH)
|
||||
#define __USE_GNU 1
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
|
||||
#define syscall_nr (offsetof(struct seccomp_data, nr))
|
||||
|
||||
#if defined(__i386__)
|
||||
#define REG_RESULT REG_EAX
|
||||
#define REG_SYSCALL REG_EAX
|
||||
#define REG_ARG0 REG_EBX
|
||||
#define REG_ARG1 REG_ECX
|
||||
#define REG_ARG2 REG_EDX
|
||||
#define REG_ARG3 REG_ESI
|
||||
#define REG_ARG4 REG_EDI
|
||||
#define REG_ARG5 REG_EBP
|
||||
#elif defined(__x86_64__)
|
||||
#define REG_RESULT REG_RAX
|
||||
#define REG_SYSCALL REG_RAX
|
||||
#define REG_ARG0 REG_RDI
|
||||
#define REG_ARG1 REG_RSI
|
||||
#define REG_ARG2 REG_RDX
|
||||
#define REG_ARG3 REG_R10
|
||||
#define REG_ARG4 REG_R8
|
||||
#define REG_ARG5 REG_R9
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_NO_NEW_PRIVS
|
||||
#define PR_SET_NO_NEW_PRIVS 38
|
||||
#endif
|
||||
|
||||
#ifndef SYS_SECCOMP
|
||||
#define SYS_SECCOMP 1
|
||||
#endif
|
||||
|
||||
static void emulator(int nr, siginfo_t *info, void *void_context)
|
||||
{
|
||||
ucontext_t *ctx = (ucontext_t *)(void_context);
|
||||
int syscall;
|
||||
char *buf;
|
||||
ssize_t bytes;
|
||||
size_t len;
|
||||
if (info->si_code != SYS_SECCOMP)
|
||||
return;
|
||||
if (!ctx)
|
||||
return;
|
||||
syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
|
||||
buf = (char *) ctx->uc_mcontext.gregs[REG_ARG1];
|
||||
len = (size_t) ctx->uc_mcontext.gregs[REG_ARG2];
|
||||
|
||||
if (syscall != __NR_write)
|
||||
return;
|
||||
if (ctx->uc_mcontext.gregs[REG_ARG0] != STDERR_FILENO)
|
||||
return;
|
||||
/* Redirect stderr messages to stdout. Doesn't handle EINTR, etc */
|
||||
ctx->uc_mcontext.gregs[REG_RESULT] = -1;
|
||||
if (write(STDOUT_FILENO, "[ERR] ", 6) > 0) {
|
||||
bytes = write(STDOUT_FILENO, buf, len);
|
||||
ctx->uc_mcontext.gregs[REG_RESULT] = bytes;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int install_emulator(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
sigset_t mask;
|
||||
memset(&act, 0, sizeof(act));
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGSYS);
|
||||
|
||||
act.sa_sigaction = &emulator;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
if (sigaction(SIGSYS, &act, NULL) < 0) {
|
||||
perror("sigaction");
|
||||
return -1;
|
||||
}
|
||||
if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
|
||||
perror("sigprocmask");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int install_filter(void)
|
||||
{
|
||||
struct sock_filter filter[] = {
|
||||
/* Grab the system call number */
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
|
||||
/* Jump table for the allowed syscalls */
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 0, 1),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
#ifdef __NR_sigreturn
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 0, 1),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
#endif
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit_group, 0, 1),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 0, 1),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 1, 0),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 3, 2),
|
||||
|
||||
/* Check that read is only using stdin. */
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDIN_FILENO, 4, 0),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
|
||||
|
||||
/* Check that write is only using stdout */
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDOUT_FILENO, 1, 0),
|
||||
/* Trap attempts to write to stderr */
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDERR_FILENO, 1, 2),
|
||||
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
|
||||
};
|
||||
struct sock_fprog prog = {
|
||||
.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, SECCOMP_MODE_FILTER, &prog)) {
|
||||
perror("prctl");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define payload(_c) (_c), sizeof((_c))
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char buf[4096];
|
||||
ssize_t bytes = 0;
|
||||
if (install_emulator())
|
||||
return 1;
|
||||
if (install_filter())
|
||||
return 1;
|
||||
syscall(__NR_write, STDOUT_FILENO,
|
||||
payload("OHAI! WHAT IS YOUR NAME? "));
|
||||
bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf));
|
||||
syscall(__NR_write, STDOUT_FILENO, payload("HELLO, "));
|
||||
syscall(__NR_write, STDOUT_FILENO, buf, bytes);
|
||||
syscall(__NR_write, STDERR_FILENO,
|
||||
payload("Error message going to STDERR\n"));
|
||||
return 0;
|
||||
}
|
||||
#else /* SUPPORTED_ARCH */
|
||||
/*
|
||||
* This sample is x86-only. Since kernel samples are compiled with the
|
||||
* host toolchain, a non-x86 host will result in using only the main()
|
||||
* below.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* SUPPORTED_ARCH */
|
102
samples/seccomp/bpf-fancy.c
Normal file
102
samples/seccomp/bpf-fancy.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Seccomp BPF example using a macro-based generator.
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Author: Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using prctl(PR_ATTACH_SECCOMP_FILTER).
|
||||
*/
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bpf-helper.h"
|
||||
|
||||
#ifndef PR_SET_NO_NEW_PRIVS
|
||||
#define PR_SET_NO_NEW_PRIVS 38
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bpf_labels l;
|
||||
static const char msg1[] = "Please type something: ";
|
||||
static const char msg2[] = "You typed: ";
|
||||
char buf[256];
|
||||
struct sock_filter filter[] = {
|
||||
/* TODO: LOAD_SYSCALL_NR(arch) and enforce an arch */
|
||||
LOAD_SYSCALL_NR,
|
||||
SYSCALL(__NR_exit, ALLOW),
|
||||
SYSCALL(__NR_exit_group, ALLOW),
|
||||
SYSCALL(__NR_write, JUMP(&l, write_fd)),
|
||||
SYSCALL(__NR_read, JUMP(&l, read)),
|
||||
DENY, /* Don't passthrough into a label */
|
||||
|
||||
LABEL(&l, read),
|
||||
ARG(0),
|
||||
JNE(STDIN_FILENO, DENY),
|
||||
ARG(1),
|
||||
JNE((unsigned long)buf, DENY),
|
||||
ARG(2),
|
||||
JGE(sizeof(buf), DENY),
|
||||
ALLOW,
|
||||
|
||||
LABEL(&l, write_fd),
|
||||
ARG(0),
|
||||
JEQ(STDOUT_FILENO, JUMP(&l, write_buf)),
|
||||
JEQ(STDERR_FILENO, JUMP(&l, write_buf)),
|
||||
DENY,
|
||||
|
||||
LABEL(&l, write_buf),
|
||||
ARG(1),
|
||||
JEQ((unsigned long)msg1, JUMP(&l, msg1_len)),
|
||||
JEQ((unsigned long)msg2, JUMP(&l, msg2_len)),
|
||||
JEQ((unsigned long)buf, JUMP(&l, buf_len)),
|
||||
DENY,
|
||||
|
||||
LABEL(&l, msg1_len),
|
||||
ARG(2),
|
||||
JLT(sizeof(msg1), ALLOW),
|
||||
DENY,
|
||||
|
||||
LABEL(&l, msg2_len),
|
||||
ARG(2),
|
||||
JLT(sizeof(msg2), ALLOW),
|
||||
DENY,
|
||||
|
||||
LABEL(&l, buf_len),
|
||||
ARG(2),
|
||||
JLT(sizeof(buf), ALLOW),
|
||||
DENY,
|
||||
};
|
||||
struct sock_fprog prog = {
|
||||
.filter = filter,
|
||||
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
|
||||
};
|
||||
ssize_t bytes;
|
||||
bpf_resolve_jumps(&l, filter, sizeof(filter)/sizeof(*filter));
|
||||
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
perror("prctl(NO_NEW_PRIVS)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
|
||||
perror("prctl(SECCOMP)");
|
||||
return 1;
|
||||
}
|
||||
syscall(__NR_write, STDOUT_FILENO, msg1, strlen(msg1));
|
||||
bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf)-1);
|
||||
bytes = (bytes > 0 ? bytes : 0);
|
||||
syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2));
|
||||
syscall(__NR_write, STDERR_FILENO, buf, bytes);
|
||||
/* Now get killed */
|
||||
syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2)+2);
|
||||
return 0;
|
||||
}
|
89
samples/seccomp/bpf-helper.c
Normal file
89
samples/seccomp/bpf-helper.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Seccomp BPF helper functions
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Author: Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using prctl(PR_ATTACH_SECCOMP_FILTER).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bpf-helper.h"
|
||||
|
||||
int bpf_resolve_jumps(struct bpf_labels *labels,
|
||||
struct sock_filter *filter, size_t count)
|
||||
{
|
||||
struct sock_filter *begin = filter;
|
||||
__u8 insn = count - 1;
|
||||
|
||||
if (count < 1)
|
||||
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))
|
||||
continue;
|
||||
switch ((filter->jt<<8)|filter->jf) {
|
||||
case (JUMP_JT<<8)|JUMP_JF:
|
||||
if (labels->labels[filter->k].location == 0xffffffff) {
|
||||
fprintf(stderr, "Unresolved label: '%s'\n",
|
||||
labels->labels[filter->k].label);
|
||||
return 1;
|
||||
}
|
||||
filter->k = labels->labels[filter->k].location -
|
||||
(insn + 1);
|
||||
filter->jt = 0;
|
||||
filter->jf = 0;
|
||||
continue;
|
||||
case (LABEL_JT<<8)|LABEL_JF:
|
||||
if (labels->labels[filter->k].location != 0xffffffff) {
|
||||
fprintf(stderr, "Duplicate label use: '%s'\n",
|
||||
labels->labels[filter->k].label);
|
||||
return 1;
|
||||
}
|
||||
labels->labels[filter->k].location = insn;
|
||||
filter->k = 0; /* fall through */
|
||||
filter->jt = 0;
|
||||
filter->jf = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Simple lookup table for labels. */
|
||||
__u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label)
|
||||
{
|
||||
struct __bpf_label *begin = labels->labels, *end;
|
||||
int id;
|
||||
if (labels->count == 0) {
|
||||
begin->label = label;
|
||||
begin->location = 0xffffffff;
|
||||
labels->count++;
|
||||
return 0;
|
||||
}
|
||||
end = begin + labels->count;
|
||||
for (id = 0; begin < end; ++begin, ++id) {
|
||||
if (!strcmp(label, begin->label))
|
||||
return id;
|
||||
}
|
||||
begin->label = label;
|
||||
begin->location = 0xffffffff;
|
||||
labels->count++;
|
||||
return id;
|
||||
}
|
||||
|
||||
void seccomp_bpf_print(struct sock_filter *filter, size_t count)
|
||||
{
|
||||
struct sock_filter *end = filter + count;
|
||||
for ( ; filter < end; ++filter)
|
||||
printf("{ code=%u,jt=%u,jf=%u,k=%u },\n",
|
||||
filter->code, filter->jt, filter->jf, filter->k);
|
||||
}
|
243
samples/seccomp/bpf-helper.h
Normal file
243
samples/seccomp/bpf-helper.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Example wrapper around BPF macros.
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Author: Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using prctl(PR_SET_SECCOMP, 2, ...).
|
||||
*
|
||||
* No guarantees are provided with respect to the correctness
|
||||
* or functionality of this code.
|
||||
*/
|
||||
#ifndef __BPF_HELPER_H__
|
||||
#define __BPF_HELPER_H__
|
||||
|
||||
#include <asm/bitsperlong.h> /* for __BITS_PER_LONG */
|
||||
#include <endian.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h> /* for seccomp_data */
|
||||
#include <linux/types.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define BPF_LABELS_MAX 256
|
||||
struct bpf_labels {
|
||||
int count;
|
||||
struct __bpf_label {
|
||||
const char *label;
|
||||
__u32 location;
|
||||
} labels[BPF_LABELS_MAX];
|
||||
};
|
||||
|
||||
int bpf_resolve_jumps(struct bpf_labels *labels,
|
||||
struct sock_filter *filter, size_t count);
|
||||
__u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label);
|
||||
void seccomp_bpf_print(struct sock_filter *filter, size_t count);
|
||||
|
||||
#define JUMP_JT 0xff
|
||||
#define JUMP_JF 0xff
|
||||
#define LABEL_JT 0xfe
|
||||
#define LABEL_JF 0xfe
|
||||
|
||||
#define ALLOW \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
|
||||
#define DENY \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
|
||||
#define JUMP(labels, label) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \
|
||||
JUMP_JT, JUMP_JF)
|
||||
#define LABEL(labels, label) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \
|
||||
LABEL_JT, LABEL_JF)
|
||||
#define SYSCALL(nr, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), \
|
||||
jt
|
||||
|
||||
/* Lame, but just an example */
|
||||
#define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label)
|
||||
|
||||
#define EXPAND(...) __VA_ARGS__
|
||||
|
||||
/* Ensure that we load the logically correct offset. */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
|
||||
#else
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
|
||||
/* Map all width-sensitive operations */
|
||||
#if __BITS_PER_LONG == 32
|
||||
|
||||
#define JEQ(x, jt) JEQ32(x, EXPAND(jt))
|
||||
#define JNE(x, jt) JNE32(x, EXPAND(jt))
|
||||
#define JGT(x, jt) JGT32(x, EXPAND(jt))
|
||||
#define JLT(x, jt) JLT32(x, EXPAND(jt))
|
||||
#define JGE(x, jt) JGE32(x, EXPAND(jt))
|
||||
#define JLE(x, jt) JLE32(x, EXPAND(jt))
|
||||
#define JA(x, jt) JA32(x, EXPAND(jt))
|
||||
#define ARG(i) ARG_32(i)
|
||||
|
||||
#elif __BITS_PER_LONG == 64
|
||||
|
||||
/* Ensure that we load the logically correct offset. */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define ENDIAN(_lo, _hi) _lo, _hi
|
||||
#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define ENDIAN(_lo, _hi) _hi, _lo
|
||||
#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
#endif
|
||||
|
||||
union arg64 {
|
||||
struct {
|
||||
__u32 ENDIAN(lo32, hi32);
|
||||
};
|
||||
__u64 u64;
|
||||
};
|
||||
|
||||
#define JEQ(x, jt) \
|
||||
JEQ64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define JGT(x, jt) \
|
||||
JGT64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define JGE(x, jt) \
|
||||
JGE64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define JNE(x, jt) \
|
||||
JNE64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define JLT(x, jt) \
|
||||
JLT64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define JLE(x, jt) \
|
||||
JLE64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
|
||||
#define JA(x, jt) \
|
||||
JA64(((union arg64){.u64 = (x)}).lo32, \
|
||||
((union arg64){.u64 = (x)}).hi32, \
|
||||
EXPAND(jt))
|
||||
#define ARG(i) ARG_64(i)
|
||||
|
||||
#else
|
||||
#error __BITS_PER_LONG value unusable.
|
||||
#endif
|
||||
|
||||
/* Loads the arg into A */
|
||||
#define ARG_32(idx) \
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx))
|
||||
|
||||
/* Loads hi into A and lo in X */
|
||||
#define ARG_64(idx) \
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \
|
||||
BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, HI_ARG(idx)), \
|
||||
BPF_STMT(BPF_ST, 1) /* hi -> M[1] */
|
||||
|
||||
#define JEQ32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 0, 1), \
|
||||
jt
|
||||
|
||||
#define JNE32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \
|
||||
jt
|
||||
|
||||
/* Checks the lo, then swaps to check the hi. A=lo,X=hi */
|
||||
#define JEQ64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JNE64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JA32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \
|
||||
jt
|
||||
|
||||
#define JA64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JGE32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \
|
||||
jt
|
||||
|
||||
#define JLT32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
|
||||
jt
|
||||
|
||||
/* Shortcut checking if hi > arg.hi. */
|
||||
#define JGE64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JLT64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JGT32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \
|
||||
jt
|
||||
|
||||
#define JLE32(value, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \
|
||||
jt
|
||||
|
||||
/* Check hi > args.hi first, then do the GE checking */
|
||||
#define JGT64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define JLE64(lo, hi, jt) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
|
||||
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
|
||||
jt, \
|
||||
BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
|
||||
|
||||
#define LOAD_SYSCALL_NR \
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
|
||||
offsetof(struct seccomp_data, nr))
|
||||
|
||||
#endif /* __BPF_HELPER_H__ */
|
68
samples/seccomp/dropper.c
Normal file
68
samples/seccomp/dropper.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Naive system call dropper built on seccomp_filter.
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Author: Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using prctl(PR_SET_SECCOMP, 2, ...).
|
||||
*
|
||||
* 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>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int install_filter(int nr, int arch, int error)
|
||||
{
|
||||
struct sock_filter filter[] = {
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
|
||||
(offsetof(struct seccomp_data, arch))),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, arch, 0, 3),
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
|
||||
(offsetof(struct seccomp_data, nr))),
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
|
||||
BPF_STMT(BPF_RET+BPF_K,
|
||||
SECCOMP_RET_ERRNO|(error & SECCOMP_RET_DATA)),
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
|
||||
};
|
||||
struct sock_fprog prog = {
|
||||
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
|
||||
.filter = filter,
|
||||
};
|
||||
if (prctl(PR_SET_SECCOMP, 2, &prog)) {
|
||||
perror("prctl");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 5) {
|
||||
fprintf(stderr, "Usage:\n"
|
||||
"dropper <syscall_nr> <arch> <errno> <prog> [<args>]\n"
|
||||
"Hint: AUDIT_ARCH_I386: 0x%X\n"
|
||||
" AUDIT_ARCH_X86_64: 0x%X\n"
|
||||
"\n", AUDIT_ARCH_I386, AUDIT_ARCH_X86_64);
|
||||
return 1;
|
||||
}
|
||||
if (install_filter(strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0),
|
||||
strtol(argv[3], NULL, 0)))
|
||||
return 1;
|
||||
execv(argv[4], &argv[4]);
|
||||
printf("Failed to execv\n");
|
||||
return 255;
|
||||
}
|
14
samples/trace_events/Makefile
Normal file
14
samples/trace_events/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
# builds the trace events example kernel modules;
|
||||
# then to use one (as root): insmod <module_name.ko>
|
||||
|
||||
# If you include a trace header outside of include/trace/events
|
||||
# then the file that does the #define CREATE_TRACE_POINTS must
|
||||
# have that tracer file in its main search path. This is because
|
||||
# define_trace.h will include it, and must be able to find it from
|
||||
# the include/trace directory.
|
||||
#
|
||||
# Here trace-events-sample.c does the CREATE_TRACE_POINTS.
|
||||
#
|
||||
CFLAGS_trace-events-sample.o := -I$(src)
|
||||
|
||||
obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace-events-sample.o
|
52
samples/trace_events/trace-events-sample.c
Normal file
52
samples/trace_events/trace-events-sample.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
/*
|
||||
* Any file that uses trace points, must include the header.
|
||||
* But only one file, must include the header by defining
|
||||
* CREATE_TRACE_POINTS first. This will make the C code that
|
||||
* creates the handles for the trace points.
|
||||
*/
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace-events-sample.h"
|
||||
|
||||
|
||||
static void simple_thread_func(int cnt)
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ);
|
||||
trace_foo_bar("hello", cnt);
|
||||
}
|
||||
|
||||
static int simple_thread(void *arg)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
while (!kthread_should_stop())
|
||||
simple_thread_func(cnt++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct task_struct *simple_tsk;
|
||||
|
||||
static int __init trace_event_init(void)
|
||||
{
|
||||
simple_tsk = kthread_run(simple_thread, NULL, "event-sample");
|
||||
if (IS_ERR(simple_tsk))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit trace_event_exit(void)
|
||||
{
|
||||
kthread_stop(simple_tsk);
|
||||
}
|
||||
|
||||
module_init(trace_event_init);
|
||||
module_exit(trace_event_exit);
|
||||
|
||||
MODULE_AUTHOR("Steven Rostedt");
|
||||
MODULE_DESCRIPTION("trace-events-sample");
|
||||
MODULE_LICENSE("GPL");
|
138
samples/trace_events/trace-events-sample.h
Normal file
138
samples/trace_events/trace-events-sample.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* If TRACE_SYSTEM is defined, that will be the directory created
|
||||
* in the ftrace directory under /sys/kernel/debug/tracing/events/<system>
|
||||
*
|
||||
* The define_trace.h below will also look for a file name of
|
||||
* TRACE_SYSTEM.h where TRACE_SYSTEM is what is defined here.
|
||||
* In this case, it would look for sample.h
|
||||
*
|
||||
* If the header name will be different than the system name
|
||||
* (as in this case), then you can override the header name that
|
||||
* define_trace.h will look up by defining TRACE_INCLUDE_FILE
|
||||
*
|
||||
* This file is called trace-events-sample.h but we want the system
|
||||
* to be called "sample". Therefore we must define the name of this
|
||||
* file:
|
||||
*
|
||||
* #define TRACE_INCLUDE_FILE trace-events-sample
|
||||
*
|
||||
* As we do an the bottom of this file.
|
||||
*
|
||||
* Notice that TRACE_SYSTEM should be defined outside of #if
|
||||
* protection, just like TRACE_INCLUDE_FILE.
|
||||
*/
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM sample
|
||||
|
||||
/*
|
||||
* Notice that this file is not protected like a normal header.
|
||||
* We also must allow for rereading of this file. The
|
||||
*
|
||||
* || defined(TRACE_HEADER_MULTI_READ)
|
||||
*
|
||||
* serves this purpose.
|
||||
*/
|
||||
#if !defined(_TRACE_EVENT_SAMPLE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_EVENT_SAMPLE_H
|
||||
|
||||
/*
|
||||
* All trace headers should include tracepoint.h, until we finally
|
||||
* make it into a standard header.
|
||||
*/
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
/*
|
||||
* The TRACE_EVENT macro is broken up into 5 parts.
|
||||
*
|
||||
* name: name of the trace point. This is also how to enable the tracepoint.
|
||||
* A function called trace_foo_bar() will be created.
|
||||
*
|
||||
* proto: the prototype of the function trace_foo_bar()
|
||||
* Here it is trace_foo_bar(char *foo, int bar).
|
||||
*
|
||||
* args: must match the arguments in the prototype.
|
||||
* Here it is simply "foo, bar".
|
||||
*
|
||||
* struct: This defines the way the data will be stored in the ring buffer.
|
||||
* There are currently two types of elements. __field and __array.
|
||||
* a __field is broken up into (type, name). Where type can be any
|
||||
* primitive type (integer, long or pointer). __field_struct() can
|
||||
* be any static complex data value (struct, union, but not an array).
|
||||
* For an array. there are three fields. (type, name, size). The
|
||||
* type of elements in the array, the name of the field and the size
|
||||
* of the array.
|
||||
*
|
||||
* __array( char, foo, 10) is the same as saying char foo[10].
|
||||
*
|
||||
* fast_assign: This is a C like function that is used to store the items
|
||||
* into the ring buffer.
|
||||
*
|
||||
* printk: This is a way to print out the data in pretty print. This is
|
||||
* useful if the system crashes and you are logging via a serial line,
|
||||
* the data can be printed to the console using this "printk" method.
|
||||
*
|
||||
* Note, that for both the assign and the printk, __entry is the handler
|
||||
* to the data structure in the ring buffer, and is defined by the
|
||||
* TP_STRUCT__entry.
|
||||
*/
|
||||
TRACE_EVENT(foo_bar,
|
||||
|
||||
TP_PROTO(char *foo, int bar),
|
||||
|
||||
TP_ARGS(foo, bar),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__array( char, foo, 10 )
|
||||
__field( int, bar )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
strlcpy(__entry->foo, foo, 10);
|
||||
__entry->bar = bar;
|
||||
),
|
||||
|
||||
TP_printk("foo %s %d", __entry->foo, __entry->bar)
|
||||
);
|
||||
#endif
|
||||
|
||||
/***** NOTICE! The #if protection ends here. *****/
|
||||
|
||||
|
||||
/*
|
||||
* There are several ways I could have done this. If I left out the
|
||||
* TRACE_INCLUDE_PATH, then it would default to the kernel source
|
||||
* include/trace/events directory.
|
||||
*
|
||||
* I could specify a path from the define_trace.h file back to this
|
||||
* file.
|
||||
*
|
||||
* #define TRACE_INCLUDE_PATH ../../samples/trace_events
|
||||
*
|
||||
* But the safest and easiest way to simply make it use the directory
|
||||
* that the file is in is to add in the Makefile:
|
||||
*
|
||||
* CFLAGS_trace-events-sample.o := -I$(src)
|
||||
*
|
||||
* This will make sure the current path is part of the include
|
||||
* structure for our file so that define_trace.h can find it.
|
||||
*
|
||||
* I could have made only the top level directory the include:
|
||||
*
|
||||
* CFLAGS_trace-events-sample.o := -I$(PWD)
|
||||
*
|
||||
* And then let the path to this directory be the TRACE_INCLUDE_PATH:
|
||||
*
|
||||
* #define TRACE_INCLUDE_PATH samples/trace_events
|
||||
*
|
||||
* But then if something defines "samples" or "trace_events" as a macro
|
||||
* then we could risk that being converted too, and give us an unexpected
|
||||
* result.
|
||||
*/
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
/*
|
||||
* TRACE_INCLUDE_FILE is not needed if the filename and TRACE_SYSTEM are equal
|
||||
*/
|
||||
#define TRACE_INCLUDE_FILE trace-events-sample
|
||||
#include <trace/define_trace.h>
|
10
samples/uhid/Makefile
Normal file
10
samples/uhid/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
||||
obj- := dummy.o
|
||||
|
||||
# List of programs to build
|
||||
hostprogs-y := uhid-example
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
always := $(hostprogs-y)
|
||||
|
||||
HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include
|
464
samples/uhid/uhid-example.c
Normal file
464
samples/uhid/uhid-example.c
Normal file
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* UHID Example
|
||||
*
|
||||
* Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* The code may be used by anyone for any purpose,
|
||||
* and can serve as a starting point for developing
|
||||
* applications using uhid.
|
||||
*/
|
||||
|
||||
/*
|
||||
* UHID Example
|
||||
* This example emulates a basic 3 buttons mouse with wheel over UHID. Run this
|
||||
* program as root and then use the following keys to control the mouse:
|
||||
* q: Quit the application
|
||||
* 1: Toggle left button (down, up, ...)
|
||||
* 2: Toggle right button
|
||||
* 3: Toggle middle button
|
||||
* a: Move mouse left
|
||||
* d: Move mouse right
|
||||
* w: Move mouse up
|
||||
* s: Move mouse down
|
||||
* r: Move wheel up
|
||||
* f: Move wheel down
|
||||
*
|
||||
* Additionally to 3 button mouse, 3 keyboard LEDs are also supported (LED_NUML,
|
||||
* LED_CAPSL and LED_SCROLLL). The device doesn't generate any related keyboard
|
||||
* events, though. You need to manually write the EV_LED/LED_XY/1 activation
|
||||
* input event to the evdev device to see it being sent to this device.
|
||||
*
|
||||
* If uhid is not available as /dev/uhid, then you can pass a different path as
|
||||
* first argument.
|
||||
* If <linux/uhid.h> is not installed in /usr, then compile this with:
|
||||
* gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c
|
||||
* And ignore the warning about kernel headers. However, it is recommended to
|
||||
* use the installed uhid.h if available.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/uhid.h>
|
||||
|
||||
/*
|
||||
* HID Report Desciptor
|
||||
* We emulate a basic 3 button mouse with wheel and 3 keyboard LEDs. This is
|
||||
* the report-descriptor as the kernel will parse it:
|
||||
*
|
||||
* INPUT(1)[INPUT]
|
||||
* Field(0)
|
||||
* Physical(GenericDesktop.Pointer)
|
||||
* Application(GenericDesktop.Mouse)
|
||||
* Usage(3)
|
||||
* Button.0001
|
||||
* Button.0002
|
||||
* Button.0003
|
||||
* Logical Minimum(0)
|
||||
* Logical Maximum(1)
|
||||
* Report Size(1)
|
||||
* Report Count(3)
|
||||
* Report Offset(0)
|
||||
* Flags( Variable Absolute )
|
||||
* Field(1)
|
||||
* Physical(GenericDesktop.Pointer)
|
||||
* Application(GenericDesktop.Mouse)
|
||||
* Usage(3)
|
||||
* GenericDesktop.X
|
||||
* GenericDesktop.Y
|
||||
* GenericDesktop.Wheel
|
||||
* Logical Minimum(-128)
|
||||
* Logical Maximum(127)
|
||||
* Report Size(8)
|
||||
* Report Count(3)
|
||||
* Report Offset(8)
|
||||
* Flags( Variable Relative )
|
||||
* OUTPUT(2)[OUTPUT]
|
||||
* Field(0)
|
||||
* Application(GenericDesktop.Keyboard)
|
||||
* Usage(3)
|
||||
* LED.NumLock
|
||||
* LED.CapsLock
|
||||
* LED.ScrollLock
|
||||
* Logical Minimum(0)
|
||||
* Logical Maximum(1)
|
||||
* Report Size(1)
|
||||
* Report Count(3)
|
||||
* Report Offset(0)
|
||||
* Flags( Variable Absolute )
|
||||
*
|
||||
* This is the mapping that we expect:
|
||||
* Button.0001 ---> Key.LeftBtn
|
||||
* Button.0002 ---> Key.RightBtn
|
||||
* Button.0003 ---> Key.MiddleBtn
|
||||
* GenericDesktop.X ---> Relative.X
|
||||
* GenericDesktop.Y ---> Relative.Y
|
||||
* GenericDesktop.Wheel ---> Relative.Wheel
|
||||
* LED.NumLock ---> LED.NumLock
|
||||
* LED.CapsLock ---> LED.CapsLock
|
||||
* LED.ScrollLock ---> LED.ScrollLock
|
||||
*
|
||||
* This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc
|
||||
* This file should print the same information as showed above.
|
||||
*/
|
||||
|
||||
static unsigned char rdesc[] = {
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x02, /* USAGE (Mouse) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
0x09, 0x01, /* USAGE (Pointer) */
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */
|
||||
0x85, 0x01, /* REPORT_ID (1) */
|
||||
0x05, 0x09, /* USAGE_PAGE (Button) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x81, 0x01, /* INPUT (Cnst,Var,Abs) */
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x30, /* USAGE (X) */
|
||||
0x09, 0x31, /* USAGE (Y) */
|
||||
0x09, 0x38, /* USAGE (WHEEL) */
|
||||
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
|
||||
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x81, 0x06, /* INPUT (Data,Var,Rel) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
0xc0, /* END_COLLECTION */
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x06, /* USAGE (Keyboard) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
0x85, 0x02, /* REPORT_ID (2) */
|
||||
0x05, 0x08, /* USAGE_PAGE (Led) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x91, 0x02, /* Output (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x91, 0x01, /* Output (Cnst,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
};
|
||||
|
||||
static int uhid_write(int fd, const struct uhid_event *ev)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(fd, ev, sizeof(*ev));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Cannot write to uhid: %m\n");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(*ev)) {
|
||||
fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int create(int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_CREATE;
|
||||
strcpy((char*)ev.u.create.name, "test-uhid-device");
|
||||
ev.u.create.rd_data = rdesc;
|
||||
ev.u.create.rd_size = sizeof(rdesc);
|
||||
ev.u.create.bus = BUS_USB;
|
||||
ev.u.create.vendor = 0x15d9;
|
||||
ev.u.create.product = 0x0a37;
|
||||
ev.u.create.version = 0;
|
||||
ev.u.create.country = 0;
|
||||
|
||||
return uhid_write(fd, &ev);
|
||||
}
|
||||
|
||||
static void destroy(int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_DESTROY;
|
||||
|
||||
uhid_write(fd, &ev);
|
||||
}
|
||||
|
||||
/* This parses raw output reports sent by the kernel to the device. A normal
|
||||
* uhid program shouldn't do this but instead just forward the raw report.
|
||||
* However, for ducomentational purposes, we try to detect LED events here and
|
||||
* print debug messages for it. */
|
||||
static void handle_output(struct uhid_event *ev)
|
||||
{
|
||||
/* LED messages are adverised via OUTPUT reports; ignore the rest */
|
||||
if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
|
||||
return;
|
||||
/* LED reports have length 2 bytes */
|
||||
if (ev->u.output.size != 2)
|
||||
return;
|
||||
/* first byte is report-id which is 0x02 for LEDs in our rdesc */
|
||||
if (ev->u.output.data[0] != 0x2)
|
||||
return;
|
||||
|
||||
/* print flags payload */
|
||||
fprintf(stderr, "LED output report received with flags %x\n",
|
||||
ev->u.output.data[1]);
|
||||
}
|
||||
|
||||
static int event(int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
ssize_t ret;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ret = read(fd, &ev, sizeof(ev));
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "Read HUP on uhid-cdev\n");
|
||||
return -EFAULT;
|
||||
} else if (ret < 0) {
|
||||
fprintf(stderr, "Cannot read uhid-cdev: %m\n");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(ev)) {
|
||||
fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case UHID_START:
|
||||
fprintf(stderr, "UHID_START from uhid-dev\n");
|
||||
break;
|
||||
case UHID_STOP:
|
||||
fprintf(stderr, "UHID_STOP from uhid-dev\n");
|
||||
break;
|
||||
case UHID_OPEN:
|
||||
fprintf(stderr, "UHID_OPEN from uhid-dev\n");
|
||||
break;
|
||||
case UHID_CLOSE:
|
||||
fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
|
||||
break;
|
||||
case UHID_OUTPUT:
|
||||
fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
|
||||
handle_output(&ev);
|
||||
break;
|
||||
case UHID_OUTPUT_EV:
|
||||
fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool btn1_down;
|
||||
static bool btn2_down;
|
||||
static bool btn3_down;
|
||||
static signed char abs_hor;
|
||||
static signed char abs_ver;
|
||||
static signed char wheel;
|
||||
|
||||
static int send_event(int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_INPUT;
|
||||
ev.u.input.size = 5;
|
||||
|
||||
ev.u.input.data[0] = 0x1;
|
||||
if (btn1_down)
|
||||
ev.u.input.data[1] |= 0x1;
|
||||
if (btn2_down)
|
||||
ev.u.input.data[1] |= 0x2;
|
||||
if (btn3_down)
|
||||
ev.u.input.data[1] |= 0x4;
|
||||
|
||||
ev.u.input.data[2] = abs_hor;
|
||||
ev.u.input.data[3] = abs_ver;
|
||||
ev.u.input.data[4] = wheel;
|
||||
|
||||
return uhid_write(fd, &ev);
|
||||
}
|
||||
|
||||
static int keyboard(int fd)
|
||||
{
|
||||
char buf[128];
|
||||
ssize_t ret, i;
|
||||
|
||||
ret = read(STDIN_FILENO, buf, sizeof(buf));
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "Read HUP on stdin\n");
|
||||
return -EFAULT;
|
||||
} else if (ret < 0) {
|
||||
fprintf(stderr, "Cannot read stdin: %m\n");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (i = 0; i < ret; ++i) {
|
||||
switch (buf[i]) {
|
||||
case '1':
|
||||
btn1_down = !btn1_down;
|
||||
ret = send_event(fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case '2':
|
||||
btn2_down = !btn2_down;
|
||||
ret = send_event(fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case '3':
|
||||
btn3_down = !btn3_down;
|
||||
ret = send_event(fd);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'a':
|
||||
abs_hor = -20;
|
||||
ret = send_event(fd);
|
||||
abs_hor = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'd':
|
||||
abs_hor = 20;
|
||||
ret = send_event(fd);
|
||||
abs_hor = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'w':
|
||||
abs_ver = -20;
|
||||
ret = send_event(fd);
|
||||
abs_ver = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 's':
|
||||
abs_ver = 20;
|
||||
ret = send_event(fd);
|
||||
abs_ver = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'r':
|
||||
wheel = 1;
|
||||
ret = send_event(fd);
|
||||
wheel = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'f':
|
||||
wheel = -1;
|
||||
ret = send_event(fd);
|
||||
wheel = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'q':
|
||||
return -ECANCELED;
|
||||
default:
|
||||
fprintf(stderr, "Invalid input: %c\n", buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
const char *path = "/dev/uhid";
|
||||
struct pollfd pfds[2];
|
||||
int ret;
|
||||
struct termios state;
|
||||
|
||||
ret = tcgetattr(STDIN_FILENO, &state);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Cannot get tty state\n");
|
||||
} else {
|
||||
state.c_lflag &= ~ICANON;
|
||||
state.c_cc[VMIN] = 1;
|
||||
ret = tcsetattr(STDIN_FILENO, TCSANOW, &state);
|
||||
if (ret)
|
||||
fprintf(stderr, "Cannot set tty state\n");
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
|
||||
fprintf(stderr, "Usage: %s [%s]\n", argv[0], path);
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
path = argv[1];
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Open uhid-cdev %s\n", path);
|
||||
fd = open(path, O_RDWR | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Create uhid device\n");
|
||||
ret = create(fd);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pfds[0].fd = STDIN_FILENO;
|
||||
pfds[0].events = POLLIN;
|
||||
pfds[1].fd = fd;
|
||||
pfds[1].events = POLLIN;
|
||||
|
||||
fprintf(stderr, "Press 'q' to quit...\n");
|
||||
while (1) {
|
||||
ret = poll(pfds, 2, -1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Cannot poll for fds: %m\n");
|
||||
break;
|
||||
}
|
||||
if (pfds[0].revents & POLLHUP) {
|
||||
fprintf(stderr, "Received HUP on stdin\n");
|
||||
break;
|
||||
}
|
||||
if (pfds[1].revents & POLLHUP) {
|
||||
fprintf(stderr, "Received HUP on uhid-cdev\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (pfds[0].revents & POLLIN) {
|
||||
ret = keyboard(fd);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
if (pfds[1].revents & POLLIN) {
|
||||
ret = event(fd);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Destroy uhid device\n");
|
||||
destroy(fd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue