Merge branch 'x86-nxt' into all-in-one
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
*.cmd
|
||||||
|
*.o
|
||||||
|
*.mod.c
|
||||||
|
*.ko
|
||||||
|
*.order
|
||||||
|
Module.symvers
|
||||||
|
.tmp_versions
|
13
README.md
@ -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
@ -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
@ -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
@ -0,0 +1 @@
|
|||||||
|
demo
|
187
x86/bpf/Makefile
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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, ®Idx, &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
@ -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
@ -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
@ -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
@ -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
@ -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描述符中的关系
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 标准三键的鼠标
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
在代码中用数据结构表示如下
|
||||||
|
|
||||||
|
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可以用一个或多个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的定义
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
下面截图是用USBlyzer抓包的SetReport数据
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以看到该请求是发送给接口0的Set Report请求(class类型的请求,参考HID Class-Specific Requests)
|
||||||
|
|
||||||
|
Set Report的参数:Report ID 0, Output Report 2
|
||||||
|
|
||||||
|
设备枚举时获取到的HID Report Descriptor截图如下
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
所以这里的Set Report请求的就是对interface 0中的Output的请求
|
||||||
|
|
||||||
|
通过bushound来手动发起这个请求进行测试(发送的数据在最下面设置,这里为0)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
修改请求的数据为3
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
bushound和usblyzer数据同步对比
|
||||||
|
|
||||||
|

|
BIN
x86/usb/comp.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
x86/usb/mouse.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
x86/usb/rd.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
x86/usb/reportid.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
x86/usb/rid.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
x86/usb/send0.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
x86/usb/send3.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
x86/usb/sr.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
x86/usb/srr.png
Normal file
After Width: | Height: | Size: 72 KiB |