Merge branch 'x86-nxt' into all-in-one

This commit is contained in:
zeroway
2022-12-04 00:02:43 +08:00
29 changed files with 3010 additions and 13 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.cmd
*.o
*.mod.c
*.ko
*.order
Module.symvers
.tmp_versions

View File

@ -1,13 +0,0 @@
# kernel_drivers_examples
## 各平台驱动实例
[RK3399](https://github.com/54shady/kernel_drivers_examples/tree/Firefly_RK3399)
[RK3288](https://github.com/54shady/kernel_drivers_examples/tree/rk3288)
[tiny4412](https://github.com/54shady/kernel_drivers_examples/tree/tiny4412)
[mini2440](https://github.com/54shady/kernel_drivers_examples/tree/mini2440)
[x86](https://github.com/54shady/kernel_drivers_examples/tree/x86)

37
x86/bitmap/Makefile Normal file
View File

@ -0,0 +1,37 @@
# Makefile
# Comment/uncomment the following line to disable/enable debugging
# DEBUG = y
# Usage
# make CROSS_COMPILE=<cross_compiler_prefix> KERNEL_DIR=<your_kernel_dir> KERNEL_BUID_OUTPUT=<kernel_buid_output>
#
# make CROSS_COMPILE=/home/zeroway/rk3399/tool/gcc-linaro-4.9.4-2017.01-i686_aarch64-linux-gnu/bin/aarch64-linux-gnu- KERNEL_DIR=/home/zeroway/rk3399/src/firefly/kernel KERNEL_BUID_OUTPUT=/home/zeroway/rk3399/src/firefly/out/target/product/rk3399_firefly_box/obj/KERNEL
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
obj-m := bitmap_test.o
KERNEL_DIR ?= /lib/modules/`uname -r`/build
KERNEL_BUID_OUTPUT ?=$(KERNEL_DIR)
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
PWD := $(shell pwd)
ARCH := x86_64
modules:
$(MAKE) -C $(KERNEL_DIR) ARCH=$(ARCH) M=$(PWD) O=$(KERNEL_DIR) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers
depend .depend dep:
$(CC) $(CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif

97
x86/bitmap/bitmap_test.c Normal file
View File

@ -0,0 +1,97 @@
#include <linux/init.h>
#include <linux/module.h>
/* https://www.cnblogs.com/schips/p/10674687.html */
#define BITMAP_LEN 10
#define USING_UNLONG
#ifdef USING_UNLONG
unsigned long mybm[1];
#else
DECLARE_BITMAP(mybm, BITMAP_LEN);
#endif
static int bitmap_test_init(void)
{
int bit;
/* architecture specifiy API */
/*
* non atomic version
* include/asm-generic/bitops/instrumented-non-atomic.h
*/
__set_bit(1, mybm);
/*
* arch___set_bit(nr, addr);
* asm volatile(__ASM_SIZE(bts) " %1,%0" : : ADDR, "Ir" (nr) : "memory");
*/
if (!test_bit(1, mybm))
printk(KERN_ALERT" bit 1 tested not set\n");
else
printk(KERN_ALERT" bit 1 tested set\n");
__clear_bit(1, mybm);
if (!test_bit(1, mybm))
printk(KERN_ALERT" bit 1 tested not set\n");
else
printk(KERN_ALERT" bit 1 tested set\n");
/*
* atomic version
* include/asm-generic/bitops/atomic.h
* arch/x86/boot/bitops.h
*/
set_bit(2, mybm);
if (!test_bit(2, mybm))
printk(KERN_ALERT" bit 2 tested not set\n");
else
printk(KERN_ALERT" bit 2 tested set\n");
clear_bit(2, mybm);
if (!test_bit(2, mybm))
printk(KERN_ALERT" bit 2 tested not set\n");
else
printk(KERN_ALERT" bit 2 tested set\n");
/* Generic API */
bitmap_zero(mybm, BITMAP_LEN);
if (!test_bit(3, mybm))
printk(KERN_ALERT" bit 3 tested not set\n");
else
printk(KERN_ALERT" bit 3 tested set\n");
bitmap_fill(mybm, BITMAP_LEN);
if (!test_bit(3, mybm))
printk(KERN_ALERT" bit 3 tested not set\n");
else
printk(KERN_ALERT" bit 3 tested set\n");
/* clear all before test */
bitmap_zero(mybm, BITMAP_LEN);
set_bit(4, mybm);
set_bit(8, mybm);
set_bit(9, mybm);
/* iterator all the setted bit */
for_each_set_bit(bit, mybm, BITMAP_LEN)
printk(KERN_ALERT" %d\n", bit);
printk(KERN_ALERT"===========\n");
/* iterator all the no setted bit */
for_each_clear_bit(bit, mybm, BITMAP_LEN)
printk(KERN_ALERT" %d\n", bit);
printk(KERN_ALERT"===========\n");
printk(KERN_ALERT"BitMap test Done\n");
return 0;
}
static void bitmap_test_exit(void)
{
printk(KERN_ALERT"BitMap test Exit\n");
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(bitmap_test_init);
module_exit(bitmap_test_exit);

1
x86/bpf/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
demo

187
x86/bpf/Makefile Normal file
View File

@ -0,0 +1,187 @@
# SPDX-License-Identifier: GPL-2.0
KERNEL_DIR ?= /lib/modules/`uname -r`/build
KERNEL_BUID_OUTPUT ?=$(KERNEL_DIR)
srctree ?=$(KERNEL_DIR)
BPF_SAMPLES_PATH ?= $(KERNEL_DIR)/samples/bpf
TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools
# List of programs to build
tprogs-y := demo
tprogs-y += vmexit
# Libbpf dependencies
LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a
demo-objs := trace_helpers.o
demo-objs += demo_user.o
vmexit-objs := trace_helpers.o
vmexit-objs += vmexit_user.o
# Tell kbuild to always build the programs
always-y := $(tprogs-y)
always-y += demo_kern.o
always-y += vmexit_kern.o
ifeq ($(ARCH), arm)
# Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux
# headers when arm instruction set identification is requested.
ARM_ARCH_SELECTOR := $(filter -D__LINUX_ARM_ARCH__%, $(KBUILD_CFLAGS))
BPF_EXTRA_CFLAGS := $(ARM_ARCH_SELECTOR)
TPROGS_CFLAGS += $(ARM_ARCH_SELECTOR)
endif
ifeq ($(ARCH), mips)
TPROGS_CFLAGS += -D__SANE_USERSPACE_TYPES__
ifdef CONFIG_MACH_LOONGSON64
BPF_EXTRA_CFLAGS += -I$(srctree)/arch/mips/include/asm/mach-loongson64
BPF_EXTRA_CFLAGS += -I$(srctree)/arch/mips/include/asm/mach-generic
endif
endif
TPROGS_CFLAGS += -Wall -O2
TPROGS_CFLAGS += -Wmissing-prototypes
TPROGS_CFLAGS += -Wstrict-prototypes
TPROGS_CFLAGS += -I$(objtree)/usr/include
TPROGS_CFLAGS += -I$(srctree)/tools/testing/selftests/bpf/
TPROGS_CFLAGS += -I$(srctree)/tools/lib/
TPROGS_CFLAGS += -I$(srctree)/tools/include
TPROGS_CFLAGS += -I$(srctree)/tools/perf
TPROGS_CFLAGS += -DHAVE_ATTR_TEST=0
ifdef SYSROOT
TPROGS_CFLAGS += --sysroot=$(SYSROOT)
TPROGS_LDFLAGS := -L$(SYSROOT)/usr/lib
endif
TPROGS_LDLIBS += $(LIBBPF) -lelf -lz
TPROGLDLIBS_tracex4 += -lrt
TPROGLDLIBS_trace_output += -lrt
TPROGLDLIBS_map_perf_test += -lrt
TPROGLDLIBS_test_overhead += -lrt
TPROGLDLIBS_xdpsock += -pthread -lcap
TPROGLDLIBS_xsk_fwd += -pthread
# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
# make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang
LLC ?= llc
CLANG ?= clang
OPT ?= opt
LLVM_DIS ?= llvm-dis
LLVM_OBJCOPY ?= llvm-objcopy
BTF_PAHOLE ?= pahole
# Detect that we're cross compiling and use the cross compiler
ifdef CROSS_COMPILE
CLANG_ARCH_ARGS = --target=$(notdir $(CROSS_COMPILE:%-=%))
endif
# Don't evaluate probes and warnings if we need to run make recursively
ifneq ($(src),)
HDR_PROBE := $(shell printf "\#include <linux/types.h>\n struct list_head { int a; }; int main() { return 0; }" | \
$(CC) $(TPROGS_CFLAGS) $(TPROGS_LDFLAGS) -x c - \
-o /dev/null 2>/dev/null && echo okay)
ifeq ($(HDR_PROBE),)
$(warning WARNING: Detected possible issues with include path.)
$(warning WARNING: Please install kernel headers locally (make headers_install).)
endif
BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
$(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
readelf -S ./llvm_btf_verify.o | grep BTF; \
/bin/rm -f ./llvm_btf_verify.o)
BPF_EXTRA_CFLAGS += -fno-stack-protector
ifneq ($(BTF_LLVM_PROBE),)
BPF_EXTRA_CFLAGS += -g
else
ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),)
BPF_EXTRA_CFLAGS += -g
LLC_FLAGS += -mattr=dwarfris
DWARF2BTF = y
endif
endif
endif
# Trick to allow make to be run from this directory
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) BPF_SAMPLES_PATH=$(BPF_SAMPLES_PATH)
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
@find $(CURDIR) -type f -name '*~' -delete
$(LIBBPF): FORCE
# Fix up variables inherited from Kbuild that tools/ build system won't like
$(MAKE) -C $(dir $@) RM='rm -rf' EXTRA_CFLAGS="$(TPROGS_CFLAGS)" \
LDFLAGS=$(TPROGS_LDFLAGS) srctree=$(BPF_SAMPLES_PATH)/../../ O=
$(obj)/syscall_nrs.h: $(obj)/syscall_nrs.s FORCE
$(call filechk,offsets,__SYSCALL_NRS_H__)
targets += syscall_nrs.s
clean-files += syscall_nrs.h
FORCE:
# Verify LLVM compiler tools are available and bpf target is supported by llc
.PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC)
verify_cmds: $(CLANG) $(LLC)
@for TOOL in $^ ; do \
if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
exit 1; \
else true; fi; \
done
verify_target_bpf: verify_cmds
@if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
echo " NOTICE: LLVM version >= 3.7.1 required" ;\
exit 2; \
else true; fi
$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
$(src)/*.c: verify_target_bpf $(LIBBPF)
$(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
$(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
$(obj)/hbm.o: $(src)/hbm.h
$(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
-include $(BPF_SAMPLES_PATH)/Makefile.target
# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
# But, there is no easy way to fix it, so just exclude it since it is
# useless for BPF samples.
# below we use long chain of commands, clang | opt | llvm-dis | llc,
# to generate final object file. 'clang' compiles the source into IR
# with native target, e.g., x64, arm64, etc. 'opt' does bpf CORE IR builtin
# processing (llvm12) and IR optimizations. 'llvm-dis' converts
# 'opt' output to IR, and finally 'llc' generates bpf byte code.
$(obj)/%.o: $(src)/%.c
@echo " CLANG-bpf " $@
$(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(BPF_EXTRA_CFLAGS) \
-I$(obj) -I$(srctree)/tools/testing/selftests/bpf/ \
-I$(srctree)/tools/lib/ \
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
-D__TARGET_ARCH_$(SRCARCH) -Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-address-of-packed-member -Wno-tautological-compare \
-Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \
-I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \
-O2 -emit-llvm -Xclang -disable-llvm-passes -c $< -o - | \
$(OPT) -O2 -mtriple=bpf-pc-linux | $(LLVM_DIS) | \
$(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@
ifeq ($(DWARF2BTF),y)
$(BTF_PAHOLE) -J $@
endif

68
x86/bpf/README.md Normal file
View File

@ -0,0 +1,68 @@
# BPF samples
## 安装头文件(make headers_install)
编译好内核并安装头文件
cd /usr/src/linux
make defconfig && make && make headers_install
## 使用demo
在当前目录操作使用对应文件
ln -s $PWD/demo_kern.c /usr/src/linux/samples/bpf
ln -s $PWD/demo_user.c /usr/src/linux/samples/bpf
ln -s $PWD/Makefile /usr/src/linux/samples/bpf
在当前目录编译时指定kernel代码路径
make KERNEL_DIR=/usr/src/linux
用file查看下编译输出文件(demo_user.o和demo_kern.o)
demo_user.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
demo_kern.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped
- demo_kern.c中的文件是被编译成eBPF的二进制文件,是要被eBPF虚拟机执行的代码
- demo_user.c是将eBPF二进制文件加载到内核的代码
## 使用内核中的samples(commit bpf: add sample usages for persistent maps/progs)
编译bpf模块
cd /usr/src/linux
make M=samples/bpf
或者在bpf目录编译
cd /usr/src/linux/samples/bpf && make
先要挂载对应的文件系统
mount -t bpf bpf /sys/fs/bpf
create map and insert elm
./fds_example -F /sys/fs/bpf/m -P -m -k 1 -v 42
bpf: map fd:3 (Success)
bpf: pin ret:(0,Success)
bpf: fd:3 u->(1:42) ret:(0,Success)
get elm
./fds_example -F /sys/fs/bpf/m -G -m -k 1
bpf: get fd:3 (Success)
bpf: fd:3 l->(1):42 ret:(0,Success)
update elm
./fds_example -F /sys/fs/bpf/m -G -m -k 1 -v 24
bpf: get fd:3 (Success)
bpf: fd:3 u->(1:24) ret:(0,Success)
get elm
./fds_example -F /sys/fs/bpf/m -G -m -k 1
bpf: get fd:3 (Success)
bpf: fd:3 l->(1):24 ret:(0,Success)

29
x86/bpf/demo_kern.c Normal file
View File

@ -0,0 +1,29 @@
#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/seccomp.h>
#include <uapi/linux/unistd.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
/*
* /sys/kernel/debug/tracing/events/syscalls/sys_enter_execve
* 通过监控内核中系统调用execve
* 当系统中调用了这个系统调用的话就会调用mybpfprog函数
*
* 编译好程序后执行./demo
*
* 然后打开一个新终端,输入一些命令即可看到结果
*/
SEC("tracepoint/syscalls/sys_enter_execve")
int mybpfprog(struct pt_regs *ctx)
{
char msg[] = "Hello, BPF World!";
bpf_trace_printk(msg, sizeof(msg));
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

51
x86/bpf/demo_user.c Normal file
View File

@ -0,0 +1,51 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include "trace_helpers.h"
int main(int argc, char *argv[])
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "mybpfprog");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}

138
x86/bpf/trace_helpers.c Normal file
View File

@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <linux/perf_event.h>
#include <sys/mman.h>
#include "trace_helpers.h"
#define DEBUGFS "/sys/kernel/debug/tracing/"
#define MAX_SYMS 300000
static struct ksym syms[MAX_SYMS];
static int sym_cnt;
static int ksym_cmp(const void *p1, const void *p2)
{
return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
}
int load_kallsyms(void)
{
FILE *f = fopen("/proc/kallsyms", "r");
char func[256], buf[256];
char symbol;
void *addr;
int i = 0;
if (!f)
return -ENOENT;
while (fgets(buf, sizeof(buf), f)) {
if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
break;
if (!addr)
continue;
syms[i].addr = (long) addr;
syms[i].name = strdup(func);
i++;
}
fclose(f);
sym_cnt = i;
qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
return 0;
}
struct ksym *ksym_search(long key)
{
int start = 0, end = sym_cnt;
int result;
/* kallsyms not loaded. return NULL */
if (sym_cnt <= 0)
return NULL;
while (start < end) {
size_t mid = start + (end - start) / 2;
result = key - syms[mid].addr;
if (result < 0)
end = mid;
else if (result > 0)
start = mid + 1;
else
return &syms[mid];
}
if (start >= 1 && syms[start - 1].addr < key &&
key < syms[start].addr)
/* valid ksym */
return &syms[start - 1];
/* out of range. return _stext */
return &syms[0];
}
long ksym_get_addr(const char *name)
{
int i;
for (i = 0; i < sym_cnt; i++) {
if (strcmp(syms[i].name, name) == 0)
return syms[i].addr;
}
return 0;
}
/* open kallsyms and read symbol addresses on the fly. Without caching all symbols,
* this is faster than load + find.
*/
int kallsyms_find(const char *sym, unsigned long long *addr)
{
char type, name[500];
unsigned long long value;
int err = 0;
FILE *f;
f = fopen("/proc/kallsyms", "r");
if (!f)
return -EINVAL;
while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) {
if (strcmp(name, sym) == 0) {
*addr = value;
goto out;
}
}
err = -ENOENT;
out:
fclose(f);
return err;
}
void read_trace_pipe(void)
{
int trace_fd;
trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
if (trace_fd < 0)
return;
while (1) {
static char buf[4096];
ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz > 0) {
buf[sz] = 0;
puts(buf);
}
}
}

39
x86/bpf/vmexit_kern.c Normal file
View File

@ -0,0 +1,39 @@
#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/seccomp.h>
#include <uapi/linux/unistd.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, int);
__type(value, int);
__uint(max_entries, 1000);
} my_map SEC(".maps");
SEC("tracepoint/kvm/kvm_exit")
int mybpfprog(struct pt_regs *ctx)
{
int *value;
int exit_count = 0;
char fmt[] = "pid: %d %d %d\n";
int pid;
pid = bpf_get_current_pid_tgid();
value = bpf_map_lookup_elem(&my_map, &pid);
if (value)
{
/* increse vmexit in each call */
exit_count = ++(*value);
bpf_trace_printk(fmt, sizeof(fmt), pid, exit_count, *value);
}
bpf_map_update_elem(&my_map, &pid, &exit_count, BPF_ANY);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

106
x86/bpf/vmexit_user.c Normal file
View File

@ -0,0 +1,106 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include "trace_helpers.h"
#define NR_VCPU 4
#define ITERAT_ALL
int main(int argc, char *argv[])
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int fd;
int cur;
#ifdef ITERAT_ALL
int next_key, lookup_key;
#else
/*
* pstree can print out each vcpu thread id
* pstree -t -p `vminfo -n demo-3 -p`
* qemu-system-x86(3286)─┬─{CPU 0/KVM}(3345)
* ├─{CPU 1/KVM}(3355)
* ├─{CPU 2/KVM}(3366)
* ├─{CPU 3/KVM}(3373)
*/
int pids[NR_VCPU] = {3345, 3355, 3366, 3373};
int prev[NR_VCPU] = {0, 0, 0, 0};
int sum = 0, delta = 0;
int i;
#endif
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "mybpfprog");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
/*
* iterating over elements in a BPF Map
*
* use `bpf_map_get_next_key` with a lookup key that doesn't exist in the map
* This forces BPF to start from the beginning of the map
*/
#if 1
fd = bpf_object__find_map_fd_by_name(obj, "my_map");
while (1)
{
#ifdef ITERAT_ALL
lookup_key = -1; /* key not exsit in map */
while (bpf_map_get_next_key(fd, &lookup_key, &next_key) == 0)
{
bpf_map_lookup_elem(fd, &lookup_key, &cur);
lookup_key = next_key;
printf("%d:%d\n", lookup_key, cur);
}
#else
for (i = 0; i < NR_VCPU; i++)
{
bpf_map_lookup_elem(fd, &pids[i], &cur);
delta = cur - prev[i];
sum += delta;
prev[i] = cur;
}
printf("vmexit total: %d\n", sum);
sum = 0;
#endif
sleep(1);
}
#else
read_trace_pipe();
#endif
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}

24
x86/crypto/Makefile Normal file
View File

@ -0,0 +1,24 @@
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
obj-m := crypto-drv.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
CC ?= gcc
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers
depend .depend dep:
$(CC) $(CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif

118
x86/crypto/README.md Normal file
View File

@ -0,0 +1,118 @@
# qemu中虚拟设备实现
参考书:How to Develop Embedded Software Using The QEMU Machine Emulator.pdf
## 设备和驱动对应的代码
crypto.c : virtual device in qemu
crypto-drv.c : driver for crypto
## 代码准备
### 将设备添加到qemu中
qemu code(将crypto.c放入hw/misc/下进行编译)
git checkout 59e1b8a22e
Compile qemu and run
./configure --target-list=x86_64-softmmu \
--extra-ldflags="`pkg-config --libs openssl`"
qemu/build/x86_64-softmmu/qemu-system-x86_64 \
-drive file=/path/to/system.qcow2 \
-smp 2 -m 1024 -enable-kvm \
-device pci-crypto,aes_cbc_256="abc" \
-netdev tap,id=nd0,ifname=tap0,script=./nat_up.py,downscript=./nat_down.py \
-device e1000,netdev=nd0,mac=52:54:00:12:34:27
### 直接在当前目录编译对应的设备驱动模块(crypto-drv.c, Makefile)
make
## 调试
### PCI寄存器的查看和修改(使用setpci)
进系统后查看pci设备寄存器(通用)
setpci --dumpregs
00 W VENDOR_ID
02 W DEVICE_ID
04 W COMMAND
06 W STATUS
08 B REVISION
09 B CLASS_PROG
...
10 L BASE_ADDRESS_0
用lspci -n 找到对应设备的BDF值(在驱动中定义了vendorId 0x2222, deviceId 0x1111)
00:03.0 00ff: 1111:2222
查看设备对应寄存器的值(比如查看VENDOR_ID, 由dumpregs可知寄存器偏移量为00)
setpci -s 00:03.0 00.w
再比如查看device id
setpci -s 00:03.0 02.w
查看设备的bar0的地址
setpci -s 00:03.0 10.l
或者使用lspci查看(下面的0xfebf1000就是bar0地址, 即mmio的地址)
lspci -vvvv -xxxx -s 00:03.0
Region 0: Memory at febf1000 (32-bit, non-prefetchable) [size=4K]
或者通过qemu monitor查看也能查看到该地址
(qemu) info qtree
(qemu) info mtree
### 使用[devmem](https://github.com/VCTLabs/devmem2)工具来调试
编译工具
gcc devmem2.c -o devmem
下面命令会调用到mmio的read函数
./devmem 0xfebf1000 b
./devmem 0xfebf1000 w
./devmem 0xfebf1000 l
写mmio空间的第二个地址0xfebf1000 + 2,命令是1:对应的是reset
./devmem 0xfebf1002 b 1
Encrypt:命令是2
./devmem 0xfebf1002 b 2
Decrypt:命令是3
./devmem 0xfebf1002 b 3
Enable interrupt
./devmem 0xfebf1003 b 2
Disable interrupt
./devmem 0xfebf1003 b 0
### 其它信息查看
查看设备对应的io空间
grep mycrypto /proc/iomem
febf1000-febf1fff : mycrypto
查看驱动对应的符号表
grep crypto_drv /proc/kallsyms

353
x86/crypto/crypto-drv.c Normal file
View File

@ -0,0 +1,353 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/bitops.h>
#include <linux/pci.h>
/* refcode: linux/drivers/net/ethernet/intel/e1000/e1000_main.c */
#define BAR_0 0
char e1000_driver_name[] = "mycrypto";
#define INTEL_E1000_ETHERNET_DEVICE(device_id) {\
PCI_DEVICE(0x1111, device_id)}
static const struct pci_device_id e1000_pci_tbl[] = {
INTEL_E1000_ETHERNET_DEVICE(0x2222),
/* required last entry */
{0,}
};
typedef enum tagIOField {
ErrorCode = 0x00,
State = 0x01,
Command = 0x02,
InterruptFlag = 0x03,
DmaInAddress = 0x04,
DmaInPagesCount = 0x08,
DmaInSizeInBytes = 0x0c,
DmaOutAddress = 0x10,
DmaOutPagesCount = 0x14,
DmaOutSizeInBytes = 0x18,
MsiErrorFlag = 0x1c,
MsiReadyFlag = 0x1d,
MsiResetFlag = 0x1e,
Unused = 0x1f,
} IoField;
#define NO2STR(n) case n: return #n
static const char *iofield2str(IoField io)
{
switch (io)
{
NO2STR(ErrorCode);
NO2STR(State);
NO2STR(Command);
NO2STR(InterruptFlag);
NO2STR(DmaInAddress);
NO2STR(DmaInPagesCount);
NO2STR(DmaInSizeInBytes);
NO2STR(DmaOutAddress);
NO2STR(DmaOutPagesCount);
NO2STR(DmaOutSizeInBytes);
NO2STR(MsiErrorFlag);
NO2STR(MsiReadyFlag);
NO2STR(MsiResetFlag);
default:
return "UnknowIoFiled";
}
}
int bit = 0; /* 0: byte, 1: word, 2: long */
int regIdx = 0;
int val = 0;
int bars;
void __iomem *hw_addr;
void *cpu_in_addr, *cpu_out_addr;
dma_addr_t dma_in_addr, dma_out_addr;
/* cat /sys/devices/pci0000:00/0000:00:03.0/mycrypto_debug */
static ssize_t mycrypto_debug_show(struct device *dev, struct device_attribute *attr, char *buf)
{
switch (bit)
{
case 0:
default:
printk("%s = 0x%x\n", iofield2str(regIdx), readb(hw_addr + regIdx));
break;
case 1:
printk("%s = 0x%x\n", iofield2str(regIdx), readw(hw_addr + regIdx));
break;
case 2:
printk("%s = 0x%x\n", iofield2str(regIdx), readl(hw_addr + regIdx));
break;
}
return 0;
}
/*
* Enable interrupt flag to 2 using devmem
* devmem 0xfebf1003 b 2
*
* for MIS#0 (LineBase INTx or signle MSI mode)
* 1: Error INT
* Enable Error INT flag
* echo 0 3 1 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*
* Trigger write ErrorCode
* echo 0 0 0 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*
* 2: Ready INT
* Enable Ready INT
* echo 0 3 2 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*
* Trigger Encrypt
* echo 0 2 2 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*
* 3: Reset INT
* Enable Reset INT
* echo 0 3 3 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*
* Trigger Encrypt
* echo 0 2 2 > /sys/devices/pci0000\:00/0000\:00\:03.0/mycrypto_debug
*/
static ssize_t mycrypto_debug_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%d %d %d", &bit, &regIdx, &val);
printk("Write [0x%x]%s = %d\n", hw_addr + regIdx, iofield2str(regIdx), val);
switch (bit)
{
case 0:
default:
/* byte write */
writeb(val & 0xf, hw_addr + regIdx);
break;
case 1:
/* word write */
writew(val & 0xff, hw_addr + regIdx);
break;
case 2:
/* long write */
writel(val, hw_addr + regIdx);
break;
}
return count;
}
static DEVICE_ATTR(mycrypto_debug, S_IRUGO | S_IWUSR, mycrypto_debug_show, mycrypto_debug_store);
static struct attribute *mycrypto_attrs[] = {
&dev_attr_mycrypto_debug.attr,
NULL,
};
static const struct attribute_group mycrypto_attr_group = {
.attrs = mycrypto_attrs,
};
typedef enum tagCryptoDeviceMSI
{
CryptoDevice_MsiZero = 0x00,
CryptoDevice_MsiError = 0x01,
CryptoDevice_MsiReady = 0x02,
CryptoDevice_MsiReset = 0x03,
CryptoDevice_MsiMax = 0x04,
} CryptoDeviceMSI;
typedef enum {
CryptoDevice_NoError,
CryptoDevice_DmaError,
CryptoDevice_DeviceHasBennReseted,
CryptoDevice_WriteIoError,
CryptoDevice_InternalError
} CryptoDeviceErrorCode;
static const char *errno2errstr(CryptoDeviceErrorCode error)
{
switch (error)
{
NO2STR(CryptoDevice_NoError);
NO2STR(CryptoDevice_DmaError);
NO2STR(CryptoDevice_DeviceHasBennReseted);
NO2STR(CryptoDevice_WriteIoError);
NO2STR(CryptoDevice_InternalError);
default:
return "UnknowError";
}
}
static irqreturn_t crypto_irq_handler(int irq, void *data)
{
int ret;
ret = readb(hw_addr + InterruptFlag);
/* read interrupt flag */
printk("Interrupt(%d) is %d -> %s\n", irq, ret, ret ? "Enable" : "Disable");
/* INTx */
if (ret == CryptoDevice_MsiError)
{
printk("ErrorINT handled\n");
printk("ErrorCode: %s\n", errno2errstr(readb(hw_addr + ErrorCode)));
}
if (ret == CryptoDevice_MsiReady)
printk("ReadyINT handled\n");
if (ret == CryptoDevice_MsiReset)
printk("ResetINT handled\n");
/* MSI#1,2,3 */
//if (readb(hw_addr + MsiErrorFlag))
// printk("MsiErrorFlag is set\n");
//if (readb(hw_addr + MsiReadyFlag))
// printk("MsiReadyFlag is set\n");
//if (readb(hw_addr + MsiResetFlag))
// printk("MsiResetFlag is set\n");
/* disable irq */
disable_irq_nosync(irq);
/* clear int */
writeb(0, hw_addr + InterruptFlag);
writeb(0, hw_addr + MsiErrorFlag);
writeb(0, hw_addr + MsiReadyFlag);
writeb(0, hw_addr + MsiResetFlag);
/* enable irq */
enable_irq(irq);
return IRQ_HANDLED;
}
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err;
bars = pci_select_bars(pdev, IORESOURCE_MEM);
printk("bars = %d, %s, %d\n", bars, __FUNCTION__, __LINE__);
err = pci_enable_device_mem(pdev);
if (err)
{
printk("%s, %d\n", "Error*****", __LINE__);
return err;
}
err = pci_request_selected_regions(pdev, bars, e1000_driver_name);
if (err)
{
printk("%s, %d\n", "error", __LINE__);
return err;
}
pci_set_master(pdev);
err = pci_save_state(pdev);
if (err)
{
printk("%s, %d\n", "error", __LINE__);
return err;
}
hw_addr = pci_ioremap_bar(pdev, BAR_0);
if (!hw_addr)
{
printk("%s, %d\n", "error", __LINE__);
return err;
}
printk("hw_addr = 0x%x\n", hw_addr);
printk("ErrorCode = 0x%x\n", readb(hw_addr + ErrorCode));
printk("State = 0x%x\n", readb(hw_addr + State));
printk("Command = 0x%x\n", readb(hw_addr + Command));
printk("InterruptFlag = 0x%x\n", readb(hw_addr + InterruptFlag));
printk("irq = %d\n", pdev->irq);
err = request_irq(pdev->irq, crypto_irq_handler, IRQF_ONESHOT, "crypto-irq", NULL);
//err = request_irq(pdev->irq, crypto_irq_handler, IRQF_PROBE_SHARED, "crypto-irq", NULL);
if (err) {
printk("Unable to allocate interrupt Error: %d\n", err);
}
/* sysfs for debug */
err = sysfs_create_group(&pdev->dev.kobj, &mycrypto_attr_group);
if (err) {
printk("failed to create sysfs device attributes\n");
return -1;
}
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (err)
{
printk("%s, %d\n", "error", __LINE__);
return err;
}
/* driver using cpu_in_addr, pass the dma_in_addr/dma_out_addr to device */
cpu_in_addr = dma_alloc_coherent(&pdev->dev, 4096, &dma_in_addr, GFP_KERNEL);
if (!cpu_in_addr)
{
printk("Failed allocation memory for DMA\n");
return -ENOMEM;
}
printk("DmaInAddress = 0x%llx, cpu_in = 0x%x\n", dma_in_addr, cpu_in_addr);
writel(dma_in_addr, hw_addr + DmaInAddress);
writel(4096, hw_addr + DmaInSizeInBytes);
writel(1, hw_addr + DmaInPagesCount);
cpu_out_addr = dma_alloc_coherent(&pdev->dev, 4096, &dma_out_addr, GFP_KERNEL);
if (!cpu_out_addr)
{
printk("Failed allocation memory for DMA\n");
return -ENOMEM;
}
printk("DmaOutAddress = 0x%llx, cpu_out = 0x%x\n", dma_out_addr, cpu_out_addr);
writel(dma_out_addr, hw_addr + DmaOutAddress);
writel(4096, hw_addr + DmaOutSizeInBytes);
writel(1, hw_addr + DmaOutPagesCount);
return 0;
}
static void e1000_remove(struct pci_dev *pdev)
{
printk("%s, %d\n", __FUNCTION__, __LINE__);
pci_release_selected_regions(pdev, bars);
iounmap(hw_addr);
sysfs_remove_group(&pdev->dev.kobj, &mycrypto_attr_group);
free_irq(pdev->irq, NULL);
dma_free_coherent(&pdev->dev, 4096, cpu_in_addr, dma_in_addr);
dma_free_coherent(&pdev->dev, 4096, cpu_out_addr, dma_out_addr);
}
static void e1000_shutdown(struct pci_dev *pdev)
{
printk("%s, %d\n", __FUNCTION__, __LINE__);
}
static struct pci_driver e1000_driver = {
.name = e1000_driver_name,
.id_table = e1000_pci_tbl,
.probe = e1000_probe,
.remove = e1000_remove,
.shutdown = e1000_shutdown,
};
static int __init e1000_init_module(void)
{
int ret;
ret = pci_register_driver(&e1000_driver);
return ret;
}
module_init(e1000_init_module);
static void __exit e1000_exit_module(void)
{
pci_unregister_driver(&e1000_driver);
}
module_exit(e1000_exit_module);
MODULE_LICENSE("GPL");

884
x86/crypto/crypto.c Normal file
View File

@ -0,0 +1,884 @@
#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h" /* iothread mutex */
#include "qapi/visitor.h"
#include <openssl/sha.h>
#define NO2STR(n) case n: return #n
#define PCI_CRYPTO_DEV(obj) OBJECT_CHECK(PCICryptoState, obj, "crypto")
#define CRYPTO_DEVICE_PAGE_SIZE 4096
#define CRYPTO_DEVICE_PAGE_MASK 999999
#define PAGE_SIZE (4096)
#define PAGE_SHIFT (12)
#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT))
#define phys_to_pfn(paddr) PHYS_PFN(paddr)
#define pfn_to_page(pfn) (void *)((pfn) * PAGE_SIZE)
#define phys_to_page(phys) (pfn_to_page(phys_to_pfn(phys)))
//#define CRYPTO_DEVICE_TO_PHYS(phys) (uint64_t)(pfn_to_page(phys_to_pfn(phys)))
//#define pfn_to_page(pfn) ((void *)((pfn) * PAGE_SIZE))
//#define phys_to_pfn(p) ((p) >> PAGE_SHIFT)
//#define phys_to_page(phys) (pfn_to_page(phys_to_pfn(phys)))
//#define CRYPTO_DEVICE_TO_PHYS(phys) (uint64_t)((pfn_to_page(phys_to_pfn(phys))))
/* DMA Buf IN Address = 64bit physical address << 12 */
#define CRYPTO_DEVICE_TO_PHYS(x) (x >> 12) //FIXME
//#define CRYPTO_DEVICE_TO_PHYS(x) (x & 0xfffffffff000) //FIXME
#define TYPE_PCI_CRYPTO_DEV "pci-crypto"
# ifdef DEBUG
# define ASSERT(cond) do { if (! (cond)) abort(); } while (0)
# else
# define ASSERT(cond) /* nothing */
# endif
//#define printf printf
enum {
CryptoDevice_ReadyState,
CryptoDevice_ResetState,
CryptoDevice_AesCbcState,
CryptoDevice_Sha2State
};
enum {
CryptoDevice_DisableFlag,
CryptoDevice_EnableFlag
};
typedef struct DmaBuf
{
uint64_t page_addr; /* address of current page */
uint32_t page_offset; /* offset in the current page */
uint32_t size;
} DmaBuf;
typedef struct DmaRequest
{
DmaBuf in;
DmaBuf out;
} DmaRequest;
/*
* mmio 地址
*
* 假设mmio基地址为0xfebf1000
* 则Command地址为 0xfebf1000 + 0x02 = 0xfebf10002
* 则InterruptFlag地址为 0xfebf1000 + 0x03 = 0xfebf10003
*
* 可以使用devmem工具来测试,不跟第三个参数表示读
*
* 从0xfebf1002开始读一个字节
* devmem 0xfebf1002 b
*
* reset
* devmem 0xfebf1002 b 1
*
* Encrypt
* devmem 0xfebf1002 b 2
*
* Decrypt
* devmem 0xfebf1002 b 3
*
* Enable interrupt flag to 2
* devmem 0xfebf1003 b 2
*/
typedef struct tagCryptoDeviceIo
{
/* 0x00 */ uint8_t ErrorCode;
/* 0x01 */ uint8_t State;
/* 0x02 */ uint8_t Command;
/* 0x03 */ uint8_t InterruptFlag; /* 1: Error INT, 2. Ready INT, 3. Reset INT */
/* 0x04 */ uint32_t DmaInAddress;
/* 0x08 */ uint32_t DmaInPagesCount;
/* 0x0c */ uint32_t DmaInSizeInBytes;
/* 0x10 */ uint32_t DmaOutAddress;
/* 0x14 */ uint32_t DmaOutPagesCount;
/* 0x18 */ uint32_t DmaOutSizeInBytes;
/* 0x1c */ uint8_t MsiErrorFlag;
/* 0x1d */ uint8_t MsiReadyFlag;
/* 0x1e */ uint8_t MsiResetFlag;
/* 0x1f */ uint8_t Unused;
} CryptoDeviceIo;
typedef enum tagIOField {
ErrorCode = 0x00,
State = 0x01,
Command = 0x02,
InterruptFlag = 0x03,
DmaInAddress = 0x04,
DmaInPagesCount = 0x08,
DmaInSizeInBytes = 0x0c,
DmaOutAddress = 0x10,
DmaOutPagesCount = 0x14,
DmaOutSizeInBytes = 0x18,
MsiErrorFlag = 0x1c,
MsiReadyFlag = 0x1d,
MsiResetFlag = 0x1e,
Unused = 0x1f,
} IoField;
typedef enum tagCryptoDeviceCommand {
CryptoDevice_IdleCommand,
CryptoDevice_ResetCommand,
CryptoDevice_AesCbcEncryptoCommand,
CryptoDevice_AesCbcDecryptoCommand,
CryptoDevice_Sha2Command
} CryptoDeviceCommand;
typedef enum tagCryptoDeviceMSI
{
CryptoDevice_MsiZero = 0x00,
CryptoDevice_MsiError = 0x01,
CryptoDevice_MsiReady = 0x02,
CryptoDevice_MsiReset = 0x03,
CryptoDevice_MsiMax = 0x04,
} CryptoDeviceMSI;
static const char *iofield2str(IoField io)
{
switch (io)
{
NO2STR(ErrorCode);
NO2STR(State);
NO2STR(Command);
NO2STR(InterruptFlag);
NO2STR(DmaInAddress);
NO2STR(DmaInPagesCount);
NO2STR(DmaInSizeInBytes);
NO2STR(DmaOutAddress);
NO2STR(DmaOutPagesCount);
NO2STR(DmaOutSizeInBytes);
NO2STR(MsiErrorFlag);
NO2STR(MsiReadyFlag);
NO2STR(MsiResetFlag);
default:
return "UnknowIoFiled";
}
}
static const char *msi2str(CryptoDeviceMSI msi)
{
switch (msi)
{
NO2STR(CryptoDevice_MsiZero);
NO2STR(CryptoDevice_MsiError);
NO2STR(CryptoDevice_MsiReady);
NO2STR(CryptoDevice_MsiReset);
NO2STR(CryptoDevice_MsiMax);
default:
return "UnknowMSI";
}
}
static const char *cmd2str(CryptoDeviceCommand cmd)
{
switch (cmd)
{
NO2STR(CryptoDevice_IdleCommand);
NO2STR(CryptoDevice_ResetCommand);
NO2STR(CryptoDevice_AesCbcEncryptoCommand);
NO2STR(CryptoDevice_AesCbcDecryptoCommand);
NO2STR(CryptoDevice_Sha2Command);
default:
return "UnknowCommand";
}
}
typedef struct PCICryptoState
{
/* < private >*/
PCIDevice parent_obj;
/* < public > */
MemoryRegion memio;
CryptoDeviceIo *io;
unsigned char memio_data[4096]; /* 4KB I/O memory (mmio) */
unsigned char aes_cbc_key[32]; /* 256 bit */
QemuMutex io_mutex;
QemuThread thread;
QemuCond thread_cond;
bool thread_running;
} PCICryptoState;
static void FillDmaRequest(PCICryptoState *dev, DmaRequest *dma)
{
dma->in.page_offset = 0;
dma->in.page_addr = CRYPTO_DEVICE_TO_PHYS(dev->io->DmaInAddress);
dma->in.size = dev->io->DmaInSizeInBytes;
dma->out.page_offset = 0;
dma->out.page_addr = CRYPTO_DEVICE_TO_PHYS(dev->io->DmaOutAddress);
dma->out.size = dev->io->DmaOutSizeInBytes;
printf("InPageAddr = 0x%lx\n", dma->in.page_addr);
printf("OutPageAddr = 0x%lx\n", dma->out.page_addr);
}
static ssize_t rw_dma_data(PCICryptoState *dev,
bool write,
DmaBuf *dma,
uint8_t *data,
uint32_t size)
{
uint32_t rw_size = 0;
printf("Dma handle %s\n", write ? "Write" : "Read");
while (0 != size)
{
if (0 == dma->size)
{
break;
}
uint64_t phys = 0;
cpu_physical_memory_read(dma->page_addr, &phys, sizeof(phys));
if (0 == phys)
{
printf("DmaPageAddr = 0x%lx, %s, %d\n", dma->page_addr, __FUNCTION__, __LINE__);
return -1;
}
phys += dma->page_offset;
const uint32_t size_to_page_end = CRYPTO_DEVICE_PAGE_SIZE
- (phys & CRYPTO_DEVICE_PAGE_MASK);
const uint32_t available_size_in_page =
MIN(size_to_page_end, dma->size);
const uint32_t size_to_rw = MIN(available_size_in_page, size);
if (write)
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
cpu_physical_memory_write(phys, data, size_to_rw);
}
else
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
cpu_physical_memory_read(phys, data, size_to_rw);
}
data += size_to_rw;
size += size_to_rw;
if (size_to_rw == size_to_page_end)
{
dma->page_addr += sizeof(uint64_t);
dma->page_offset = 0;
}
else
{
dma->page_offset += size_to_rw;
}
dma->size -= size_to_rw;
rw_size += size_to_rw;
}
return rw_size;
}
/*
* There are three types of interrupt
* 1. LineBase (INTx)
* 2. MSI
* 3. MSI-X
*
* InterruptMode:
* In our device, we implement the first two type (with three mode)
* 1. Line-based(if system doesn't support MSIs)
* 2. One MSI(if the system can't allocate more than one MSI)
* 3. Multiple MSIs(if the system can allocate all requested MSIs
* and more than one is requested)
*/
static void raise_interrupt(PCICryptoState *dev, CryptoDeviceMSI msi)
{
const uint8_t msi_flag = (1u << msi) >> 1u;
ASSERT(msi != CryptoDevice_MsiZero);
printf("About to raise interrupt %s\n", msi2str(msi));
if (0 == (dev->io->InterruptFlag & msi_flag))
{
printf("Interrupt is disabled\n");
return;
}
qemu_mutex_unlock(&dev->io_mutex);
if (msi_enabled(&dev->parent_obj))
{
/* checks the number of allocated MSIs */
if (CryptoDevice_MsiMax != msi_nr_vectors_allocated(&dev->parent_obj))
{
printf("Send MSI 0 (origin msi = %u) allocated msi %u\n",
msi,
msi_nr_vectors_allocated(&dev->parent_obj));
/*
* InterruptMode2
* if not all of the requested interrupts were allocated,
* set the interrupt type with MSI#0
*/
msi = CryptoDevice_MsiZero;
}
else
{
printf("(InterruptMode3) Send MSI %u\n", msi);
}
msi_notify(&dev->parent_obj, msi);
}
else
{
printf("MSI not enable, Raise legacy interrupt\n");
pci_set_irq(&dev->parent_obj, 1);
}
qemu_mutex_lock(&dev->io_mutex);
}
typedef enum {
CryptoDevice_NoError,
CryptoDevice_DmaError,
CryptoDevice_DeviceHasBennReseted,
CryptoDevice_WriteIoError,
CryptoDevice_InternalError
} CryptoDeviceErrorCode;
static const char *errno2errstr(CryptoDeviceErrorCode error)
{
switch (error)
{
NO2STR(CryptoDevice_NoError);
NO2STR(CryptoDevice_DmaError);
NO2STR(CryptoDevice_DeviceHasBennReseted);
NO2STR(CryptoDevice_WriteIoError);
NO2STR(CryptoDevice_InternalError);
default:
return "UnknowError";
}
}
static void raise_error_int(PCICryptoState *dev, CryptoDeviceErrorCode error)
{
printf("%s>>> Generate %s\n", __FUNCTION__, errno2errstr(error));
ASSERT(error <= 0xff);
dev->io->ErrorCode = (uint8_t)error;
dev->io->MsiErrorFlag = 1;
raise_interrupt(dev, CryptoDevice_MsiError);
}
static void raise_ready_int(PCICryptoState *dev)
{
dev->io->MsiReadyFlag = 1;
raise_interrupt(dev, CryptoDevice_MsiReady);
}
static void raise_reset_int(PCICryptoState *dev)
{
dev->io->MsiResetFlag = 1;
raise_interrupt(dev, CryptoDevice_MsiReset);
}
static uint64_t pci_crypto_memio_read(void *opaque,
hwaddr addr,
unsigned size)
{
uint64_t res = 0;
PCICryptoState *dev = (PCICryptoState *)opaque;
if (addr >= sizeof(dev->memio_data))
{
printf("Read from unknow IO offset 0x%lx\n", addr);
return 0;
}
if (addr + size >= sizeof(dev->memio_data))
{
printf("Read from IO offset 0x%lx but bad size %d\n", addr, size);
return 0;
}
qemu_mutex_lock(&dev->io_mutex);
switch (size)
{
case sizeof(uint8_t):
res = *(uint8_t *)&dev->memio_data[addr];
printf("Read I/O memroy [%s] size %d, value = 0x%lx\n",
iofield2str(addr), size, res);
break;
case sizeof(uint16_t):
res = *(uint16_t *)&dev->memio_data[addr];
printf("Read I/O memroy [%s] size %d, value = 0x%lx\n",
iofield2str(addr), size, res);
break;
case sizeof(uint32_t):
res = *(uint32_t *)&dev->memio_data[addr];
printf("Read I/O memroy [%s] size %d, value = 0x%lx\n",
iofield2str(addr), size, res);
break;
}
qemu_mutex_unlock(&dev->io_mutex);
return res;
}
static void clear_interrupt(PCICryptoState *dev)
{
if (!msi_enabled(&dev->parent_obj))
{
printf("MIS not enabled\n");
if (0 == dev->io->MsiErrorFlag &&
0 == dev->io->MsiReadyFlag &&
0 == dev->io->MsiResetFlag)
{
printf("Clear legacy interrupt\n");
pci_set_irq(&dev->parent_obj, 0);
}
}
}
static void pci_crypto_memio_write(void *opaque,
hwaddr addr,
uint64_t val,
unsigned size)
{
PCICryptoState *dev = (PCICryptoState *)opaque;
if (addr >= sizeof(dev->memio_data))
{
printf("Write to unknow IO offset 0x%lx\n", addr);
return;
}
if (addr + size >= sizeof(dev->memio_data))
{
printf("Write to IO offset 0x%lx but bad size %d\n", addr, size);
return;
}
qemu_mutex_lock(&dev->io_mutex);
#define CASE($field) \
case offsetof(CryptoDeviceIo, $field): \
ASSERT(size == sizeof(dev->io->$field));
printf("Write I/O memory[%s] size %d, value = 0x%lx\n", iofield2str(addr), size, val);
switch (addr)
{
CASE(ErrorCode)
raise_error_int(dev, CryptoDevice_WriteIoError);
break;
CASE(State)
raise_error_int(dev, CryptoDevice_WriteIoError);
break;
CASE(Command)
dev->io->Command = (uint8_t)val;
switch (dev->io->Command)
{
case CryptoDevice_ResetCommand:
case CryptoDevice_AesCbcEncryptoCommand:
case CryptoDevice_AesCbcDecryptoCommand:
case CryptoDevice_Sha2Command:
qemu_cond_signal(&dev->thread_cond);
break;
default:
ASSERT(!"Unexpected command value\n");
raise_error_int(dev, CryptoDevice_WriteIoError);
}
break;
CASE(InterruptFlag)
dev->io->InterruptFlag = (uint8_t)val;
break;
CASE(DmaInAddress)
dev->io->DmaInAddress = (uint32_t)val;
break;
CASE(DmaInPagesCount)
dev->io->DmaInPagesCount = (uint32_t)val;
break;
CASE(DmaInSizeInBytes)
dev->io->DmaInSizeInBytes = (uint32_t)val;
break;
CASE(DmaOutAddress)
dev->io->DmaOutAddress = (uint32_t)val;
break;
CASE(DmaOutPagesCount)
dev->io->DmaOutPagesCount = (uint32_t)val;
break;
CASE(DmaOutSizeInBytes)
dev->io->DmaOutSizeInBytes = (uint32_t)val;
break;
CASE(MsiErrorFlag)
dev->io->MsiErrorFlag = (uint8_t)val;
clear_interrupt(dev);
break;
CASE(MsiReadyFlag)
dev->io->MsiReadyFlag = (uint8_t)val;
clear_interrupt(dev);
break;
CASE(MsiResetFlag)
dev->io->MsiResetFlag = (uint8_t)val;
clear_interrupt(dev);
break;
}
#undef CASE
qemu_mutex_unlock(&dev->io_mutex);
}
static const MemoryRegionOps pci_crypto_memio_ops = {
.read = pci_crypto_memio_read,
.write = pci_crypto_memio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 4,
},
.valid = {
.min_access_size = 1,
.max_access_size = 4,
},
};
static void DoReset(PCICryptoState *dev)
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
dev->io->ErrorCode = CryptoDevice_NoError;
dev->io->State = CryptoDevice_ReadyState;
dev->io->Command = CryptoDevice_IdleCommand;
dev->io->DmaInAddress = 0;
dev->io->DmaInPagesCount = 0;
dev->io->DmaInSizeInBytes = 0;
dev->io->DmaOutAddress = 0;
dev->io->DmaOutPagesCount = 0;
dev->io->DmaOutSizeInBytes = 0;
raise_reset_int(dev);
}
static bool CheckStop(PCICryptoState *dev)
{
bool res = false;
qemu_mutex_lock(&dev->io_mutex);
if (CryptoDevice_ResetCommand == dev->io->Command ||
!dev->thread_running)
{
DoReset(dev);
res = true;
}
qemu_mutex_unlock(&dev->io_mutex);
return res;
}
/* FIXME
* REF: crypto/aes.c AES_cbc_encrypto
*/
static int DoAesCbc(PCICryptoState *dev, DmaRequest *dma, bool encrypt)
{
/* TODO */
if (encrypt)
printf("(%s: %d)>>> Do Encrypt \n", __FUNCTION__, __LINE__);
else
printf("(%s: %d)>>> Do Decrypt \n", __FUNCTION__, __LINE__);
return 0;
}
static int DoSha256(PCICryptoState *dev, DmaRequest *dma)
{
unsigned char digest[SHA256_DIGEST_LENGTH] = {};
unsigned char page[CRYPTO_DEVICE_PAGE_SIZE] = {};
SHA256_CTX hash = {};
if (!dma->out.page_addr || dma->out.size <
SHA256_DIGEST_LENGTH)
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
return CryptoDevice_DmaError;
}
if (!dma->in.page_addr && dma->in.size != 0)
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
return CryptoDevice_DmaError;
}
SHA256_Init(&hash);
while (0 != dma->in.size)
{
ssize_t size = rw_dma_data(dev,
false,
&dma->in, page, sizeof(page));
if (-1 == size)
{
printf("%s, %d\n", __FUNCTION__, __LINE__);
return CryptoDevice_DmaError;
}
SHA256_Update(&hash, page, size);
if (CheckStop(dev))
{
return CryptoDevice_DeviceHasBennReseted;
}
}
SHA256_Final(digest, &hash);
if (sizeof(digest) != rw_dma_data(dev,
true,
&dma->out, digest, sizeof(digest)))
{
return CryptoDevice_DmaError;
}
return CryptoDevice_NoError;
}
static void *worker_thread(void *pdev)
{
PCICryptoState *dev = (PCICryptoState *)pdev;
qemu_mutex_lock(&dev->io_mutex);
printf("worker thread started\n");
for (;;)
{
while (CryptoDevice_IdleCommand == dev->io->Command
&& dev->thread_running)
{
printf("Thread idling...Zzz\n");
qemu_cond_wait(&dev->thread_cond, &dev->io_mutex);
}
if (!dev->thread_running)
{
printf("worker thread stopped\n");
return NULL;
}
if (CryptoDevice_IdleCommand != dev->io->Command)
{
int error = 0;
DmaRequest dma = {};
printf("Thread working on %s\n", cmd2str(dev->io->Command));
FillDmaRequest(dev, &dma);
switch (dev->io->Command)
{
case CryptoDevice_ResetCommand:
dev->io->State = CryptoDevice_ResetState;
DoReset(dev);
error = CryptoDevice_DeviceHasBennReseted;
break;
case CryptoDevice_AesCbcEncryptoCommand:
dev->io->State = CryptoDevice_AesCbcState;
qemu_mutex_unlock(&dev->io_mutex);
error = DoAesCbc(dev, &dma, true);
qemu_mutex_lock(&dev->io_mutex);
break;
case CryptoDevice_AesCbcDecryptoCommand:
dev->io->State = CryptoDevice_AesCbcState;
qemu_mutex_unlock(&dev->io_mutex);
error = DoAesCbc(dev, &dma, false);
qemu_mutex_lock(&dev->io_mutex);
break;
case CryptoDevice_Sha2Command:
dev->io->State = CryptoDevice_Sha2State;
qemu_mutex_unlock(&dev->io_mutex);
error = DoSha256(dev, &dma);
qemu_mutex_lock(&dev->io_mutex);
break;
}
switch (error)
{
case CryptoDevice_DeviceHasBennReseted:
break;
case CryptoDevice_NoError:
printf("Line:%d, Device No Error ==> set readyflag\n", __LINE__);
raise_ready_int(dev);
break;
case CryptoDevice_DmaError:
case CryptoDevice_InternalError:
raise_error_int(dev, error);
break;
default:
printf("Unexpected error status %d\n", error);
raise_error_int(dev, error);
}
dev->io->State = CryptoDevice_ReadyState;
dev->io->Command = CryptoDevice_IdleCommand;
}
}
ASSERT(!"Never execute");
}
static void pci_crypto_realize(PCIDevice *pci_dev, Error **errp)
{
PCICryptoState *dev = PCI_CRYPTO_DEV(pci_dev);
printf("pci_crypto_realize\n");
memory_region_init_io(&dev->memio,
OBJECT(dev),
&pci_crypto_memio_ops,
dev,
"pci-crypto-mmio",
sizeof(dev->memio_data));
pci_register_bar(pci_dev,
0,
PCI_BASE_ADDRESS_SPACE_MEMORY,
&dev->memio);
pci_config_set_interrupt_pin(pci_dev->config, 1);
if (msi_init(pci_dev, 0, CryptoDevice_MsiMax, true, false, errp))
{
printf("Cannot init MSI\n");
}
dev->thread_running = true;
dev->io = (CryptoDeviceIo *)dev->memio_data;
memset(dev->memio_data, 0, sizeof(dev->memio_data));
qemu_mutex_init(&dev->io_mutex);
qemu_cond_init(&dev->thread_cond);
qemu_thread_create(&dev->thread,
"crypto-device-woker",
worker_thread,
dev,
QEMU_THREAD_JOINABLE);
}
static void pci_crypto_uninit(PCIDevice *pci_dev)
{
PCICryptoState *dev = PCI_CRYPTO_DEV(pci_dev);
printf("pci_crypto_uninit\n");
qemu_mutex_lock(&dev->io_mutex);
dev->thread_running = false;
qemu_mutex_unlock(&dev->io_mutex);
qemu_cond_signal(&dev->thread_cond);
qemu_thread_join(&dev->thread);
qemu_cond_destroy(&dev->thread_cond);
qemu_mutex_destroy(&dev->io_mutex);
}
static void crypto_set_aes_cbc_key_256(Object *obj,
const char *value,
Error **errp)
{
PCICryptoState *dev = PCI_CRYPTO_DEV(obj);
/* calc sha256 from the user string*/
SHA256((const unsigned char *)value, strlen(value), dev->aes_cbc_key);
printf("%s, %d\n", __FUNCTION__, __LINE__);
}
static void pci_crypto_reset(DeviceState *pci_dev)
{
PCICryptoState *dev = PCI_CRYPTO_DEV(pci_dev);
printf("pci_crypto_reset\n");
qemu_mutex_lock(&dev->io_mutex);
dev->io->ErrorCode = CryptoDevice_NoError;
dev->io->State = CryptoDevice_ReadyState;
dev->io->Command = CryptoDevice_IdleCommand;
dev->io->InterruptFlag = CryptoDevice_DisableFlag;
dev->io->DmaInAddress = 0;
dev->io->DmaInPagesCount = 0;
dev->io->DmaInSizeInBytes = 0;
dev->io->DmaOutAddress = 0;
dev->io->DmaOutPagesCount = 0;
dev->io->DmaOutSizeInBytes = 0;
dev->io->MsiErrorFlag = 0;
dev->io->MsiReadyFlag = 0;
dev->io->MsiResetFlag = 0;
qemu_mutex_unlock(&dev->io_mutex);
}
static void pci_crypto_instance_init(Object *obj)
{
PCICryptoState *dev = PCI_CRYPTO_DEV(obj);
printf("pci_crypto_instance_init\n");
memset(dev->aes_cbc_key, 0, sizeof(dev->aes_cbc_key));
object_property_add_str(obj, "aes_cbc_256",
NULL,
crypto_set_aes_cbc_key_256);
}
static void pci_crypto_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
printf("pci_crypt_class_init\n");
//k->is_express = false;
k->realize = pci_crypto_realize;
k->exit = pci_crypto_uninit;
k->vendor_id = 0x1111;
k->device_id = 0x2222;
k->revision = 0x00;
k->class_id = PCI_CLASS_OTHERS;
dc->desc = "PCI Crypto Device";
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->reset = pci_crypto_reset;
dc->hotpluggable = false;
}
static void pci_crypto_register_types(void)
{
static InterfaceInfo interfaces[] = {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
{ },
};
static const TypeInfo pci_crypto_info = {
.name = TYPE_PCI_CRYPTO_DEV,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCICryptoState),
.instance_init = pci_crypto_instance_init,
.class_init = pci_crypto_class_init,
.interfaces = interfaces,
};
type_register_static(&pci_crypto_info);
}
type_init(pci_crypto_register_types)

158
x86/crypto/devmem2.c Normal file
View File

@ -0,0 +1,158 @@
/*
* devmem2.c: Simple program to read/write from/to any location in memory.
*
* Copyright (C) 2000, Jan-Derk Bakker (jdb@lartmaker.nl)
*
*
* This software has been developed for the LART computing board
* (http://www.lart.tudelft.nl/). The development has been sponsored by
* the Mobile MultiMedia Communications (http://www.mmc.tudelft.nl/)
* and Ubiquitous Communications (http://www.ubicom.tudelft.nl/)
* projects.
*
*
* 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
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
static inline void *fixup_addr(void *addr, size_t size);
int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
unsigned long read_result, write_val;
off_t target;
int access_type = 'w';
char fmt_str[128];
size_t data_size;
if(argc < 2) {
fprintf(stderr, "\nUsage:\t%s { address } [ type [ data ] ]\n"
"\taddress : memory address to act upon\n"
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord, [l]ong\n"
"\tdata : data to be written\n\n",
argv[0]);
exit(1);
}
target = strtoul(argv[1], 0, 0);
if(argc > 2)
access_type = tolower(argv[2][0]);
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
printf("/dev/mem opened.\n");
fflush(stdout);
/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
virt_addr = map_base + (target & MAP_MASK);
switch(access_type) {
case 'b':
data_size = sizeof(unsigned char);
virt_addr = fixup_addr(virt_addr, data_size);
read_result = *((unsigned char *) virt_addr);
break;
case 'h':
data_size = sizeof(unsigned short);
virt_addr = fixup_addr(virt_addr, data_size);
read_result = *((unsigned short *) virt_addr);
break;
case 'w':
data_size = sizeof(uint32_t);
virt_addr = fixup_addr(virt_addr, data_size);
read_result = *((uint32_t *) virt_addr);
break;
case 'l':
data_size = sizeof(uint64_t);
virt_addr = fixup_addr(virt_addr, data_size);
read_result = *((uint64_t *) virt_addr);
break;
default:
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
exit(2);
}
sprintf(fmt_str, "Read at address 0x%%08lX (%%p): 0x%%0%dlX\n", 2*data_size);
printf(fmt_str, (unsigned long)target, virt_addr, read_result);
fflush(stdout);
if(argc > 3) {
write_val = strtoul(argv[3], 0, 0);
switch(access_type) {
case 'b':
virt_addr = fixup_addr(virt_addr, sizeof(unsigned char));
*((unsigned char *) virt_addr) = write_val;
read_result = *((unsigned char *) virt_addr);
break;
case 'h':
virt_addr = fixup_addr(virt_addr, sizeof(unsigned short));
*((unsigned short *) virt_addr) = write_val;
read_result = *((unsigned short *) virt_addr);
break;
case 'w':
virt_addr = fixup_addr(virt_addr, sizeof(uint32_t));
*((uint32_t *) virt_addr) = write_val;
read_result = *((uint32_t *) virt_addr);
break;
case 'l':
virt_addr = fixup_addr(virt_addr, sizeof(uint64_t));
*((uint64_t *) virt_addr) = write_val;
read_result = *((uint64_t *) virt_addr);
break;
}
sprintf(fmt_str, "Write at address 0x%%08lX (%%p): 0x%%0%dlX, "
"readback 0x%%0%dlX\n", 2*data_size, 2*data_size);
printf(fmt_str, (unsigned long)target, virt_addr,
write_val, read_result);
fflush(stdout);
}
if(munmap(map_base, MAP_SIZE) == -1) FATAL;
close(fd);
return 0;
}
static inline void *fixup_addr(void *addr, size_t size)
{
#ifdef FORCE_STRICT_ALIGNMENT
unsigned long aligned_addr = (unsigned long)addr;
aligned_addr &= ~(size - 1);
addr = (void *)aligned_addr;
#endif
return addr;
}

37
x86/hlist/Makefile Normal file
View File

@ -0,0 +1,37 @@
# Makefile
# Comment/uncomment the following line to disable/enable debugging
# DEBUG = y
# Usage
# make CROSS_COMPILE=<cross_compiler_prefix> KERNEL_DIR=<your_kernel_dir> KERNEL_BUID_OUTPUT=<kernel_buid_output>
#
# make CROSS_COMPILE=/home/zeroway/rk3399/tool/gcc-linaro-4.9.4-2017.01-i686_aarch64-linux-gnu/bin/aarch64-linux-gnu- KERNEL_DIR=/home/zeroway/rk3399/src/firefly/kernel KERNEL_BUID_OUTPUT=/home/zeroway/rk3399/src/firefly/out/target/product/rk3399_firefly_box/obj/KERNEL
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
obj-m := demo.o
KERNEL_DIR ?= /lib/modules/`uname -r`/build
KERNEL_BUID_OUTPUT ?=$(KERNEL_DIR)
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
PWD := $(shell pwd)
ARCH := x86_64
modules:
$(MAKE) -C $(KERNEL_DIR) ARCH=$(ARCH) M=$(PWD) O=$(KERNEL_DIR) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers
depend .depend dep:
$(CC) $(CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif

71
x86/hlist/demo.c Normal file
View File

@ -0,0 +1,71 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
/* refercence with kernel module zd1201 */
/* https://zhuanlan.zhihu.com/p/82375193 */
struct AAAAA {
/* hash table implement in list address */
struct hlist_head nodelist;
};
struct AAAAA_node {
struct hlist_node fnode;
char name[20];
};
struct AAAAA *module5a;
static int hlist_test_init(void)
{
struct AAAAA_node *node1;
struct AAAAA_node *node2;
struct AAAAA_node *pos;
/* another hlist_node to use as temporary storage */
struct hlist_node *tmp_node;
module5a = kmalloc(sizeof(struct AAAAA), GFP_ATOMIC);
if (!module5a)
return -1;
node1 = kmalloc(sizeof(*node1), GFP_ATOMIC);
if (!node1)
return -1;
strcpy(node1->name, "Node1");
node2 = kmalloc(sizeof(*node2), GFP_ATOMIC);
if (!node2)
return -1;
strcpy(node2->name, "Node2");
/* init hash table */
INIT_HLIST_HEAD(&module5a->nodelist);
/* add node to hash table */
hlist_add_head(&node1->fnode, &module5a->nodelist);
hlist_add_head(&node2->fnode, &module5a->nodelist);
hlist_for_each_entry_safe(pos, tmp_node, &module5a->nodelist, fnode) {
printk(KERN_ALERT"Iter node : %s\n", pos->name);
}
return 0;
}
static void hlist_test_exit(void)
{
struct AAAAA_node *pos;
struct hlist_node *tmp_node;
hlist_for_each_entry_safe(pos, tmp_node, &module5a->nodelist, fnode) {
printk(KERN_ALERT"Delete node : %s\n", pos->name);
hlist_del_init(&pos->fnode);
kfree(pos);
}
kfree(module5a);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(hlist_test_init);
module_exit(hlist_test_exit);

605
x86/usb/README.md Normal file
View File

@ -0,0 +1,605 @@
# USB HID Class
[参考文章 usb hid report descriptors](https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/)
[Device Class Definition for Human Interface Devices(HID)](hid1_11.pdf)
hid report descriptors在usb描述符中的关系
![report descriptors](./reportid.png)
## 标准三键的鼠标
![3 buttons mouse](./mouse.png)
在代码中用数据结构表示如下
struct mouse_report_t
{
uint8_t buttons;
int8_t x;
int8_t y;
}
在hid report descriptors中3个按键描述如下
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)
每个按键用两个值来表示(0和1)
LOGICAL MINIMUM (0)
LOGICAL MAXIMUM (1)
总共有3个按键
REPORT_COUNT (3) # 每个按键值是单独上报的,需要上报3次按键信息
REPORT_SIZE (1) # 每次上报的大小为1bit
上报数据描述
INPUT (Data, Var, Abs) # 2
所以按键的所有描述合起来就是如下
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_COUNT (3)
REPORT_SIZE (1)
INPUT (Data,Var,Abs)
由于上报数据需要按照8bit对齐,所以剩余的未使用的5bit数据也需要描述
REPORT_COUNT (1) # 只需要上报一次
REPORT_SIZE (5) # 一次性上报5bit数据
INPUT (Cnst,Var,Abs) # 3
或者也可以
REPORT_COUNT (5) # 上报5次
REPORT_SIZE (1) # 每次上报1bit数据
INPUT (Cnst,Var,Abs) # 3
x轴的数据上报(y轴同理)
USAGE_PAGE (Generic Desktop)
USAGE (X)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8) # 一次上报8bit数据
REPORT_COUNT (1) # 上报一次
INPUT (Data,Var,Rel)
可以将xy轴的数据合并起来表示
USAGE_PAGE (Generic Desktop)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8) # 每次上报8bit数据
REPORT_COUNT (2) # 需要上报2次
INPUT (Data,Var,Rel)
按键和xy轴合起来如下
USAGE_PAGE (Button)
USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM (Button 3)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_COUNT (3)
REPORT_SIZE (1)
INPUT (Data,Var,Abs)
REPORT_COUNT (1)
REPORT_SIZE (5)
INPUT (Cnst,Var,Abs)
USAGE_PAGE (Generic Desktop)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM (127)
REPORT_SIZE (8)
REPORT_COUNT (2)
INPUT (Data,Var,Rel)
还需要添加一些描述信息来表明是鼠标
USAGE_PAGE (Generic Desktop)
USAGE (Mouse)
COLLECTION (Application)
USAGE (Pointer)
COLLECTION (Physical)
... # 将之前的信息填在这里
END COLLECTION
END COLLECTION
所以代码中就出现了如下代码(如下代码来源qemu中的dev-hid.c)
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
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) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x03, /* Report Count (3) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
}
qemu代码里描述上述鼠标按键的结构时HIDPointerEvent
typedef struct HIDPointerEvent {
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
int32_t dz, buttons_state;
} HIDPointerEvent;
能否让一个鼠标同时支持绝对和相对坐标(通过配置两个report id)
需要在上报的第一个byte里填充report id就能达到效果
typedef struct HIDPointerEvent {
int8_t report_id;
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
int32_t dz, buttons_state;
} HIDPointerEvent;
在report descriptors中添加report id的描述
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x01, /* report id 1 */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
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) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) 未配置button的位也需要上报 */
0x75, 0x05, /* Report Size (5) 一次性将剩余的5bit都上报 */
0x81, 0x01, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x03, /* Report Count (3) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
}
![report id format](./rid.png)
其中格式解读如下(report id可以用一个或多个byte来表示)
report_tag(1000 01nn) + nbyte_report_id
如果用一个byte表示,则nn = 01, report id tag就是 1000 0101 ==> 0x85
则表示report id 1结果如下
0x85, 0x01, /* report id 1 */
上面的修改只是修改来report descriptors上报数据的格式需要上报report id
,在实际上报的usb数据里也需要修改能上报report id,修改如下
鼠标坐标的处理函数是(hid_pointer_event)
在相对坐标和绝对坐标中分别上报不同的report id(需要和report descriptors一致,将tablet的直接全部拷贝即可)
hid_pointer_event
case INPUT_EVENT_KIND_REL:
e->report_id = 1;
case INPUT_EVENT_KIND_ABS:
e->report_id = 2;
在usb写数据到端点时,在最前面的第一个byte填充report id
hid_pointer_poll
case HID_MOUSE:
if (len > l) {
buf[l++] = e->report_id;
}
相对鼠标和绝对鼠标report descriptors
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x01, /* report id 1 */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
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) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x03, /* Report Count (3) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x02, /* report id 2 */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
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) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
0x35, 0x00, /* Physical Minimum (0) */
0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x35, 0x00, /* Physical Minimum (same as logical) */
0x45, 0x00, /* Physical Maximum (same as logical) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
};
## 标准五键(两个侧键)鼠标
鼠标按键用button1, button2, button3...来命名
qemu代码中用结构体HIDPointerEvent来描述鼠标的数据
typedef struct HIDPointerEvent {
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
int32_t dz, buttons_state;
} HIDPointerEvent;
其中buttons_state每一位表示按键,这里用int32_t说明可以支持32个按键(一般支持8个按键即可uint8_t)
bit1对应button1, bit2对应button2,以此类推
一般操作系统中将对应按键映射成不同功能
button1:left(鼠标左键)
button2:right(鼠标右键)
button3:middle(滚轮按键)
button4:side(侧边按键1)
button5:extra(侧边按键2)
鼠标设备上报buttonN经过系统映射后变为对应的功能按键(left,right,middle...)
大部分标准5键鼠标都是按照上面的进行映射(button4,button5的映射非强制)
所以在qemu模拟的鼠标中可以将button4和button5和side和extra对应以达到同大部分物理鼠标效果
### 下面以spice-input和USB HID鼠标为例说明
spice捕获坐标后传递给qemu的input子系统来作为鼠标数据提供给虚拟鼠标设备
SpiceInput(spice-input.c)->InputCore(input.c)->USB HID Device(hid.c)
SpiceInput中通过button_mask来传入鼠标按键掩码值
- 传入对应按键的掩码表示该按键按下(Press)
- 传入0表示该按键抬起(Release)
下面代将button4配置为side, button5配置为extra
```c
static void spice_update_buttons(QemuSpicePointer *pointer,
int wheel, uint32_t button_mask)
{
static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01, /* button 1 */
[INPUT_BUTTON_MIDDLE] = 0x04, /* button 3 */
[INPUT_BUTTON_RIGHT] = 0x02, /* button 2 */
[INPUT_BUTTON_WHEEL_UP] = 0x40, /* button 7 */
[INPUT_BUTTON_WHEEL_DOWN] = 0x80, /* button 8 */
[INPUT_BUTTON_SIDE] = 0x08, /* button 4 */
[INPUT_BUTTON_EXTRA] = 0x10, /* button 5 */
};
if (wheel < 0) {
button_mask |= 0x40;
}
if (wheel > 0) {
button_mask |= 0x80;
}
```
qemu中通过文件qapi/ui.json来生成头文件中对应的按键枚举
```c
{ 'enum' : 'InputButton',
'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side',
'extra' ] }
下面是生成的对应的枚举值
typedef enum InputButton {
INPUT_BUTTON_LEFT = 0,
INPUT_BUTTON_MIDDLE = 1,
INPUT_BUTTON_RIGHT = 2,
INPUT_BUTTON_WHEEL_UP = 3,
INPUT_BUTTON_WHEEL_DOWN = 4,
INPUT_BUTTON_SIDE = 5,
INPUT_BUTTON_EXTRA = 6,
INPUT_BUTTON__MAX = 7,
} InputButton;
```
SpiceInput调用InputCore的接口最终调入对应的设备模拟设备处理函数,这里假设是HID设备(hid_pointer_event)
```c
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
static const int bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_RIGHT] = 0x02,
[INPUT_BUTTON_MIDDLE] = 0x04,
[INPUT_BUTTON_SIDE] = 0x08,
[INPUT_BUTTON_EXTRA] = 0x10,
};
```
还有一个需要注意的是hid report descriptor
其中下面的配置是同时上报8个按键
```c
static const uint8_t qemu_tablet_hid_report_descriptor[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (1) */
0x29, 0x08, /* Usage Maximum (8) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x08, /* Report Count (8) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
0x35, 0x00, /* Physical Minimum (0) */
0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x35, 0x00, /* Physical Minimum (same as logical) */
0x45, 0x00, /* Physical Maximum (same as logical) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
};
/* HID描述符长度需要对应修改 */
static const USBDescIface desc_iface_tablet = {
...
/* HID descriptor */
.data = (uint8_t[]) {
0x09, /* u8 bLength */
USB_DT_HID, /* u8 bDescriptorType */
0x01, 0x00, /* u16 HID_class */
0x00, /* u8 country_code */
0x01, /* u8 num_descriptors */
USB_DT_REPORT, /* u8 type: Report */
sizeof(qemu_tablet_hid_report_descriptor) / sizeof(uint8_t), 0, /* u16 len */
},
...
/* 比较合理的做法是根据设备能支持的按键数来配置, 如下是标准五键鼠标 */
static const uint8_t qemu_tablet_hid_report_descriptor[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xa1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xa1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (1) */
0x29, 0x05, /* Usage Maximum (5) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0X81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x81, 0x03, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
0x35, 0x00, /* Physical Minimum (0) */
0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-0x7f) */
0x25, 0x7f, /* Logical Maximum (0x7f) */
0x35, 0x00, /* Physical Minimum (same as logical) */
0x45, 0x00, /* Physical Maximum (same as logical) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xc0, /* End Collection */
0xc0, /* End Collection */
};
```
### 调试输入数据
确认SpiceInput输出的数据可以在InputCore中打开调试
(qemu) trace-event input_event_btn on
or
virsh qemu-monitor-command <domain> --hmp trace-event input_event_btn on
确认模拟HID鼠标上报的数据,在下面函数中用gdb打印对应数据
USB数据包的处理函数usb_handle_packet调用流程如下
```c
usb_handle_packet
usb_hid_handle_data
hid_pointer_poll
int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
case HID_TABLET:
if (len > l) {
buf[l++] = e->buttons_state;
}
if (len > l) {
buf[l++] = dx & 0xff;
}
if (len > l) {
buf[l++] = dx >> 8;
}
if (len > l) {
buf[l++] = dy & 0xff;
}
if (len > l) {
buf[l++] = dy >> 8;
}
if (len > l) {
buf[l++] = dz;
}
break;
(gdb) printf "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x 0x%02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]
```
其中使用USBlyzer抓到的raw data区域对应的数据为上面的buf
## How to retrieve the report descriptors in linux
[Ref: get usb report descriptor](https://www.slashdev.ca/2010/05/08/get-usb-report-descriptor-with-linux/)
先确认设备的路径(比如这里的1-8:1.1)
cat /proc/bus/input/devices
Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.1/0003:24AE:1813.0010/input/input40
使用lsusb查看设备的report descriptors显示如下结果
lsusb -vd 24ae:1813
Report Descriptors:
** UNAVAILABLE **
可以先unbind设备再查看
bash -c "echo -n 1-8:1.1 >/sys/bus/usb/drivers/usbhid/unbind"
lsusb -vd 24ae:1813
或者在debug目录中查看对应的report descriptors(如果有该文件的话)
cat /sys/kernel/debug/hid/0003\:24AE\:1813.0010/rdesc
实时查看input event信息(和使用evtest一样)
cat /sys/kernel/debug/hid/0003\:24AE\:1813.0010/event
## HID set report example(USB 键盘中按下CapsLock,NumLock等LED按键)
HID协议中对set report的定义
![set report request](./srr.png)
下面截图是用USBlyzer抓包的SetReport数据
![set report](./sr.png)
可以看到该请求是发送给接口0的Set Report请求(class类型的请求,参考HID Class-Specific Requests)
Set Report的参数:Report ID 0, Output Report 2
设备枚举时获取到的HID Report Descriptor截图如下
![HID report descriptor](./rd.png)
所以这里的Set Report请求的就是对interface 0中的Output的请求
通过bushound来手动发起这个请求进行测试(发送的数据在最下面设置,这里为0)
![manual set report](./send0.png)
修改请求的数据为3
![manual set report](./send3.png)
bushound和usblyzer数据同步对比
![compare](./comp.png)

BIN
x86/usb/comp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
x86/usb/mouse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
x86/usb/rd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
x86/usb/reportid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
x86/usb/rid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
x86/usb/send0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
x86/usb/send3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
x86/usb/sr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
x86/usb/srr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB