- 工具信息
- 主机:VMwareWorkstation或VirtualBox + CentOS Linux release 8.5.2111
VMwareWorkstation或VirtualBox
自行安装
CentOS Linux release 8.5.2111
https://mirrors.aliyun.com/centos/8/isos/x86_64/
选择:CentOS-8.5.2111-x86_64-dvd1.iso
- 虚拟机:qemu-7.1.0
https://download.qemu.org/qemu-7.1.0.tar.xz
- 内核:linux-4.19.99
https://mirrors.aliyun.com/linux-kernel/v4.x/linux-4.19.99.tar.xz
- Busy Box:busybox-1.36.0
https://busybox.net/downloads/busybox-1.36.0.tar.bz2
- 目的
在主机CentOS(VMware虚拟机)上通过qemu-7.1.0启动添加了PCIE调试信息后编译的linux-4.19.99,结合qemu-7.1.0提供的e1000搭建PCIE的学习环境,对内核涉及到的PCIE文件做简单介绍。本文仅仅是搭建PCIE学习环境,不包括PCIE的理论介绍。最终效果如下:
(1) Qemu启动加了调试信息的内核 /home/jis1/qemu-7.1.0/build/qemu-system-x86_64 \ -smp 1 \ -m 1024M -kernel /home/jis1/linux-4.19.99/x86_64/arch/x86/boot/bzImage \ -nographic \ -L /home/jis1/qemu-7.1.0/pc-bios/ -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc pci=noacpi" \ -nic tap,id=tape0,ifname=tap0,script=no,downscript=no \ -initrd /home/jis1/busybox-1.36.0/ramdisk.gz \ -device virtio-net-pci,id=net1,mac=00:00:00:00:00:02,mq=on \
(2) 启动过程中有添加的调试信息(本文目的) [ 3.972627] pci 0000:00:02.0: BAR 2: assigned [mem 0xfec01000-0xfec01fff] [ 3.979393] [ 3.979393] pcibios_bus_to_resource-86: window->res = [io 0x0000-0xffff] [ 3.987623] [ 3.987623] pcibios_bus_to_resource-86: window->res = [mem 0x00000000-0xffffffffff] [ 3.994463] [ 3.994463] pci_assign_resource-329: BAR 1: [mem size 0x00001000] [ 4.001766] pci 0000:00:04.0: BAR 1: assigned [mem 0xfec02000-0xfec02fff]
(3) lspci能查看网卡e1000的信息 / # lspci -k 00:04.0 00:01.0 Class 0601: 8086:7000 00:04.0 Class 0200: 1af4:1000 virtio-pci 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e e1000 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111 |
- 环境搭建步骤
- 安装主机CentOS
可以使用物理机,也可以使用VMwareWorkstation + CentOS或VirtualBox + CentOS模拟,这里不详述安装步骤。安装后的CentOS信息如下:
[root@localhost jis1]# cat /etc/os-release NAME="CentOS Stream" VERSION="8" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="8" PLATFORM_ID="platform:el8" PRETTY_NAME="CentOS Stream 8" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:8" HOME_URL="https://centos.org/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8" REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream" [root@localhost jis1]# uname -a Linux localhost.localdomain 4.18.0-408.el8.x86_64 #1 SMP Mon Jul 18 17:42:52 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux [root@localhost jis1]# |
- 安装依赖的工具
- 基础工具
[root@localhost jis1]# yum -y install git automake libtool glib2 glib2-devel [root@localhost jis1]#yum install ncurses-devel bison flex openssl-devel perl elfutils-libelf-devel |
- 编译安装re2c
[root@localhost jis1]# wget https://down.24kplus.com/linux/re2c-1.1.1.tar.gz
[root@localhost jis1]# tar xf re2c-1.1.1.tar.gz [root@localhost jis1]# cd re2c-1.1.1/ [root@localhost re2c-1.1.1]# git init [root@localhost re2c-1.1.1]# ./autogen.sh [root@localhost re2c-1.1.1]# ./configure [root@localhost re2c-1.1.1]# make && make install [root@localhost re2c-1.1.1]# re2c -v re2c 1.1.1 [root@localhost re2c-1.1.1]# |
- 编译安装ninja
[root@localhost jis1]#wget https://codeload.github.com/ninja-build/ninja/tar.gz/refs/tags/v1.10.2 [root@localhost jis1]# mv v1.10.2 ninja-1.10.2.tar.gz [root@localhost jis1]# tar xf ninja-1.10.2.tar.gz [root@localhost jis1]# cd ninja-1.10.2/ |
查看可执行文件python3所在的目录,修改configure.py,把“#!/usr/bin/env python”替换成python3所在的目录“#/usr/bin/python3”
[root@localhost ninja-1.10.2]# which python3 /usr/bin/python3 [root@localhost ninja-1.10.2]# vi configure.py #!/usr/bin/env python // 替换成“#/usr/bin/python3” … |
编译安装ninja:
[root@localhost ninja-1.10.2]# ./configure.py –bootstrap [root@localhost ninja-1.10.2]# cp ninja /usr/bin/ [root@localhost ninja-1.10.2]# ninja --version 1.10.2 [root@localhost ninja-1.10.2]# |
- 编译安装qemu
从官网下载qemu模拟器(https://download.qemu.org/qemu-7.1.0.tar.xz)。
[root@localhost jis1]# tar xf qemu-7.1.0.tar.xz [root@localhost jis1]# cd qemu-7.1.0/ [root@localhost qemu-7.1.0]# mkdir build [root@localhost qemu-7.1.0]# cd build [root@localhost build]# ../configure --target-list=x86_64-softmmu,x86_64-linux-user --python=/usr/bin/python3 [root@localhost build]# make [root@localhost build]# ./qemu-system-x86_64 --version QEMU emulator version 7.1.0 Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers |
注:这里使用参数“--target-list”仅编译x86_64的qemu工具。
编译过程中如果遇到问题可以参考如下网址解决:
https://blog.csdn.net/yzx19/article/details/123423668
- 编译内核
从官网下载内核版本:https://mirrors.aliyun.com/linux-kernel/v4.x/linux-4.19.99.tar.xz
- 生成默认编译选项.config文件
[root@localhost jis1]# tar xf linux-4.19.99.tar.xz [root@localhost jis1]# cd linux-4.19.99/ [root@localhost linux-4.19.99]# make O=x86_64 x86_64_defconfig [root@localhost linux-4.19.99]# ls -a x86_64/ |grep config .config [root@localhost linux-4.19.99]# |
- 配置使用ramdisk
[root@localhost linux-4.19.99]# make O=x86_64 menuconfig
General setup ---> ----> [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support Device Drivers ---> [*] Block devices ---> <*> RAM block device support (65536) Default RAM disk size (kbytes) |
- 编译生成bzImage
[root@localhost linux-4.19.99]# make O=x86_64 bzImage //经过一段时间的编译,生成了bzImage
[root@localhost linux-4.19.99]# ls x86_64/arch/x86/boot/ |grep bz bzImage |
- 制作根文件系统
- 从官网下载内核版本并编译:
[root@localhost jis1]# wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2 [root@localhost jis1]# tar xf busybox-1.36.0.tar.bz2 [root@localhost jis1]# cd busybox-1.36.0/ [root@localhost busybox-1.36.0]#make menuconfig // 选择编译成静态 Settings ---> Build Options ---> [*] Build BusyBox as a static binary (no shared libs) [root@localhost busybox-1.36.0]#make [root@localhost busybox-1.36.0]#make install |
- 新生成文件“sh”,输入如下内容
#!/bin/bash sudo rm -rf rootfs sudo rm -rf tmpfs sudo rm -rf ramdisk* sudo mkdir rootfs sudo cp ../busybox*/_install/* rootfs/ -raf sudo mkdir -p rootfs/proc/ sudo mkdir -p rootfs/sys/ sudo mkdir -p rootfs/tmp/ sudo mkdir -p rootfs/root/ sudo mkdir -p rootfs/var/ sudo mkdir -p rootfs/mnt/ if [ ! -d proc ] && [ ! -d sys ] && [ ! -d dev ] && [ ! -d etc/init.d ]; then mkdir proc sys dev etc etc/init.d fi
if [ -f etc/init.d/rcS ]; then rm etc/init.d/rcS fi echo "#!/bin/sh" > etc/init.d/rcS echo "mount -t proc none /proc" >> etc/init.d/rcS echo "mount -t sysfs none /sys" >> etc/init.d/rcS echo "/sbin/mdev -s" >> etc/init.d/rcS chmod +x etc/init.d/rcS
sudo cp etc rootfs/ -arf sudo mkdir -p rootfs/lib sudo cp -arf /lib/i386-linux-gnu/* rootfs/lib/ sudo rm rootfs/lib/*.a sudo strip rootfs/lib/* sudo mkdir -p rootfs/dev/ sudo mknod rootfs/dev/tty1 c 4 1 sudo mknod rootfs/dev/tty2 c 4 2 sudo mknod rootfs/dev/tty3 c 4 3 sudo mknod rootfs/dev/tty4 c 4 4 sudo mknod rootfs/dev/console c 5 1 sudo mknod rootfs/dev/null c 1 3 sudo dd if=/dev/zero of=ramdisk bs=1M count=32 sudo mkfs.ext4 -F ramdisk sudo mkdir -p tmpfs sudo mount -t ext4 ramdisk ./tmpfs/ -o loop sudo cp -raf rootfs/* tmpfs/ sudo umount tmpfs sudo gzip --best -c ramdisk > ramdisk.gz |
- 使用脚本生成根文件系统
[root@localhost busybox-1.36.0]# chmod +x create_rootfs.sh [root@localhost busybox-1.36.0]# ./create_rootfs.sh
[root@localhost busybox-1.36.0]# ll |grep ramdisk.gz -rw-r--r--. 1 root root 1587035 Mar 25 13:39 ramdisk.gz |
- 启动内核
- 使用如下qemu命令启动内核
/home/jis1/qemu-7.1.0/build/qemu-system-x86_64 \ -smp 1 \ -m 1024M -kernel /home/jis1/linux-4.19.99/x86_64/arch/x86/boot/bzImage \ -nographic \ -L /home/jis1/qemu-7.1.0/pc-bios/ -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc pci=noacpi" \ -nic tap,id=tape0,ifname=tap0,script=no,downscript=no \ -initrd /home/jis1/busybox-1.36.0/ramdisk.gz \ -device virtio-net-pci,id=net1,mac=00:00:00:00:00:02,mq=on \ |
注:要指定“-L /home/jis1/qemu-7.1.0/pc-bios/”,不然会报错:
qemu: could not load PC BIOS 'bios-256k.bin' |
- qemu启动编译好的内核后,可以查看pci设备
~ # uname -a Linux (none) 4.19.99 #1 SMP Sat Mar 25 10:36:50 CST 2023 x86_64 GNU/Linux ~ # lspci 00:01.0 Class 0601: 8086:7000 00:04.0 Class 0200: 1af4:1000 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111 ~ # |
这里的“00:04.0 Class 0200: 1af4:1000”就是e1000网卡的模拟,可以用来调试和学习PCIE。
- PCIE调试简单介绍
- PCIE驱动框架
在开始通过代码来学习PCIE之前,需要对PCIE的概念及驱动框架有基本的了解,网上介绍的比较多,推荐一个介绍得比较好的博客:
https://www.cnblogs.com/LoyenWang/p/14165852.html
如下是我总结的PCIE宏观驱动框架:
- 系统启动时会注册驱动框架总线pci_bus_type(见drivers\pci\pci-driver.c),pci_bus_type和驱动框架中的其它总线一样,维护设备链表和驱动链表:当有设备节点或驱动节点插入时,pci_bus_type中的match函数pci_bus_match()用于匹配设备和驱动,如果匹配,调用pci_bus_type中的probe函数pci_device_probe()处理。
- “drivers\pci\controller”下有各种PCIE控制器的驱动程序,如pcie-cadence-host.c是针对cadence的PCIE控制器的驱动、pcie-xilinx-nwl.c是针对赛灵思nwl控制器的驱动;这些驱动会注册到pci_bus_type总线的驱动链表。系统启动过程中通过设备树生成对应设备节点(platform_device)并注册到pci_bus_type总线的设备链表,驱动框架调用match函数pci_bus_match匹配成功后,调用pci_pus_type的probe函数pci_device_probe处理,该probe函数最终找到PCIE控制器的probe函数(如赛灵思PCIE控制器的probe函数nwl_pcie_probe()).
- PCIE控制器的probe函数对控制器进行必要的设置(各厂家的PCIE控制器有区别)后,最终统一调用函数pci_scan_root_bus_bridge()从0号总线使用DFS算法开始扫描生成pci_dev、pci_bus、pci_slot等与具体设备相关的数据结构。扫描过程中通过调用pci_scan_child_bus() => pci_scan_child_bus_extend()=>pci_scan_single_device()=>pci_device_add()将一个PCIE设备(结构体:pci_dev)加入到驱动框架的设备链表中(,如果有对应的驱动链表上注册过PCIE设备驱动,则调用PCIE设备驱动的probe函数,probe函数一般的作用为“设置芯片寄存器、挂接中断处理函数、提供用户操作接口fops等”,probe函数结束后该PCIE设备就可以正常工作了。
- 调试介绍
在掌握了PCIE概念和驱动框架后,可以通过代码进行学习,最常用的就是添加打印信息,比如在如下函数中添加打印后,重新编译内核并使用qemu启动新编译的内核就可以看到自己添加的打印信息了。
添加调试信息:
启动过程中打印的调试信息:
(1) Qemu启动加了调试信息的内核 /home/jis1/qemu-7.1.0/build/qemu-system-x86_64 \ -smp 1 \ -m 1024M -kernel /home/jis1/linux-4.19.99/x86_64/arch/x86/boot/bzImage \ -nographic \ -L /home/jis1/qemu-7.1.0/pc-bios/ -append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc pci=noacpi" \ -nic tap,id=tape0,ifname=tap0,script=no,downscript=no \ -initrd /home/jis1/busybox-1.36.0/ramdisk.gz \ -device virtio-net-pci,id=net1,mac=00:00:00:00:00:02,mq=on \
(2) 启动过程中有添加的调试信息(本文目的) [ 3.972627] pci 0000:00:02.0: BAR 2: assigned [mem 0xfec01000-0xfec01fff] [ 3.979393] [ 3.979393] pcibios_bus_to_resource-86: window->res = [io 0x0000-0xffff] [ 3.987623] [ 3.987623] pcibios_bus_to_resource-86: window->res = [mem 0x00000000-0xffffffffff] [ 3.994463] [ 3.994463] pci_assign_resource-329: BAR 1: [mem size 0x00001000] [ 4.001766] pci 0000:00:04.0: BAR 1: assigned [mem 0xfec02000-0xfec02fff]
(3) lspci能查看网卡e1000的信息 / # lspci -k 00:04.0 00:01.0 Class 0601: 8086:7000 00:04.0 Class 0200: 1af4:1000 virtio-pci 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e e1000 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111 |
小技巧:Qemu是作为一个用户态进程使用的,使用过程中要结束qemu进程,可以kill进程的方式。在另外一个终端可以使用如下命令结束qemu进程:
ps aux |grep qemu |awk '{print $2}' |xargs kill -9 |
添加什么样的调试信息需要基于对代码的理解和具体需求,调试信息最好结合lspci一起使用,然而busybox自带的lspci显示的信息比较少,这就需要自己编译lspci工具了,下一篇会介绍lspci的编译和使用,如下是自编译的lspci的显示信息:
# lspci -vvv -s 00:04.0 00:04.0 Class 0200: Device 1af4:1000 Subsystem: Device 1af4:0001 Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx+ Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0 Interrupt: pin A routed to IRQ 11 Region 1: Memory at fec02000 (32-bit, non-prefetchable) [size=4K] Region 4: Memory at 180000000 (64-bit, prefetchable) [size=16K] Expansion ROM at fec80000 [disabled] [size=256K] Capabilities: [98] MSI-X: Enable+ Count=4 Masked- Vector table: BAR=1 offset=00000000 PBA: BAR=1 offset=00000800 Capabilities: [84] Vendor Specific Information: VirtIO: <unknown> BAR=0 offset=00000000 size=00000000 Capabilities: [70] Vendor Specific Information: VirtIO: Notify BAR=4 offset=00003000 size=00001000 multiplier=00000004 Capabilities: [60] Vendor Specific Information: VirtIO: DeviceCfg BAR=4 offset=00002000 size=00001000 Capabilities: [50] Vendor Specific Information: VirtIO: ISR BAR=4 offset=00001000 size=00001000 Capabilities: [40] Vendor Specific Information: VirtIO: CommonCfg BAR=4 offset=00000000 size=00001000 Kernel driver in use: virtio-pci |