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 |