使用QEMU模拟CXL设备
本文只介绍环境搭建过程,CXL设备的使用,之后再说
总览
研发的目标平台是CXL3.0,目前物理设备尚未生产出来,使用QEMU来模拟CXL设备
Linux内核对于CXL的支持情况
| 功能 | 最低内核版本 | 说明 |
|---|
| CXL 总线与 Type 3 内存设备基本支持 | v5.17 | 初步引入 cxl_core 子系统,但功能有限 |
| CXL 内存作为 System RAM 注册 | v6.3 | 可通过 add_memory() 将 CXL 内存加入 buddy allocator |
| 完整 DAX 支持(/dev/daxX.Y) | v6.4–6.6 | 需配合 cxl_pmem 和 device-dax |
| ACPI CDAT/HMAT 解析(用于 QEMU 模拟) | v6.6+ | QEMU 通过 ACPI 表描述 CXL 拓扑 |
| CXL 2.0/3.0 交换机与多端口模拟支持 | v6.8+ | 更好支持 QEMU 的 CXL 拓扑建模 |
| 稳定性和调试工具(cxl-cli, sysfs 接口) | v6.9+ | 用户态工具链成熟 |
QEMU对于CXL的支持情况
| QEMU 版本 | 发布时间 | CXL 支持亮点 |
|---|
| 7.2 | 2022 年底 | 初步支持 CXL 2.0 Type 3 内存设备(基础仿真) |
| 8.0 | 2023 年中 | 重大扩展:HDM 解码器、CXL 交换机、内存交织 |
| 8.1–8.2 | 2023–2024 | 稳定性提升,ACPI 表支持(CDAT/HMAT) |
| 9.0 | 2024 年中 | 支持 Type 1/2 设备、MMIO 缓存、NUMA 拓扑 |
| 9.1 | 2024 年底 | CXL 3.0 Type 3 设备、动态容量设备(DCD)初步支持 |
| 10.0–10.1 | 2025 年 | CXL 3.0 全面支持、热插拔、嵌套虚拟化、多层交换 |
选用Linux Kernel 6.11 和 QEMU 10.1.3
依赖安装
1 2 3 4 5 6 7 8
| sudo apt-get install libglib2.0-dev libgcrypt20-dev zlib1g-dev \ autoconf automake libtool bison flex libpixman-1-dev bc \ make ninja-build libncurses-dev libelf-dev libssl-dev debootstrap \ libcap-ng-dev libattr1-dev libslirp-dev libslirp0
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison
pip3 install tomli
|
QEMU编译安装
QEMU环境好配,设置目标平台为x86_64,并且启用debug模式,方便断点调试Kernel
1 2 3 4 5 6 7 8 9 10 11 12 13
| git clone -b v10.1.3 https://github.com/qemu/qemu.git
git clone --depth 1 --single-branch --no-tags -b v10.1.3 https://github.com/qemu/qemu.git
cd qemu ./configure --target-list=x86_64-softmmu --enable-debug make -j$(nproc)
alias qemu='~/tool/qemu/build/qemu-system-x86_64'
|
Linux Kernel
Kernel的配置项较为复杂,本来想直接使用CXL-Emulator-QEMU这个仓库中的.config文件,但是作者太粗糙了,打包了所有的驱动,众所周知,驱动是内核最大的一坨屎山,编译时间巨长,只能自己配置。
推荐使用menuconfig界面,启用PCIE,CXL,DAX三个选项
1 2 3
| git clone --depth 1 --single-branch --no-tags -b v6.11 https://github.com/torvalds/linux.git
make menuconfig
|
如果想直接修改.config,先make localmodconfig根据主机信息来配置驱动和模块,然后复制下面的选项,或者使用scripts/config --enable 选项名的形式逐个添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!-- 修改.config文件 --> CONFIG_PCIEAER_CXL=y CONFIG_CXL_BUS=y CONFIG_CXL_PCI=y CONFIG_CXL_MEM_RAW_COMMANDS=y CONFIG_CXL_ACPI=y CONFIG_CXL_PMEM=y CONFIG_CXL_MEM=y CONFIG_CXL_PORT=y CONFIG_CXL_SUSPEND=y CONFIG_CXL_REGION=y CONFIG_CXL_REGION_INVALIDATION_TEST=y
CONFIG_DWC_PCIE_PMU=y CONFIG_CXL_PMU=y
CONFIG_DAX=y CONFIG_DEV_DAX=y CONFIG_DEV_DAX_PMEM=y CONFIG_DEV_DAX_HMEM=y CONFIG_DEV_DAX_CXL=y CONFIG_DEV_DAX_HMEM_DEVICES=y CONFIG_DEV_DAX_KMEM=y CONFIG_NVMEM=y CONFIG_NVMEM_SYSFS=y
|
编译安装
1 2 3
| make -j$(nproc) ls arch/x86_64/boot/bzImage -lh sudo make modules_install
|
QEMU启动
在本章节一共需要使用三个目录,QEMU_PATH,KERNEL_PATH,DISK_PATH
为客户机创建根文件系统:
1 2 3 4 5 6 7 8 9 10 11
| cd $(QEMU_PATH)
./build/qemu-img create qemu-cxl-img 40G
sudo mkfs.ext4 qemu-cxl-img
mkdir ~/qemu-cxl-mnt
sudo mount -o loop ./qemu-cxl-img ~/qemu-cxl-mnt
sudo debootstrap --arch amd64 noble ~/qemu-cxl-mnt
|
设置虚拟机和主机互通:
修改用户名为你的用户名
1 2 3 4 5 6 7 8
| echo "#! /bin/bash mount -t 9p -o trans=virtio homeshare /home/zmy mount -t 9p -o trans=virtio modshare /lib/modules " > ./rc.local chmod a+x ./rc.local sudo mv ./rc.local ~/qemu-cxl-mnt/etc/ sudo mkdir -p ~/qemu-cxl-mnt/home/zmy sudo mkdir -p ~/qemu-cxl-mnt/lib/modules/
|
设置网络:
1 2 3
| sudo mkdir -p ~/qemu-cxl-mnt/etc/netplan vim -p ~/qemu-cxl-mnt/etc/netplan/config.yaml ll ~/qemu-cxl-mnt/etc/netplan/config.yaml
|
其中config.yaml文件内容如下:
1 2 3 4 5 6
| network: version: 2 renderer: networkd ethernets: enp0s2: dhcp4: true
|
设置虚拟机用户密码: 之后就使用root用户登录虚拟机镜像
1 2 3
| sudo chroot ~/qemu-cxl-mnt passwd exit
|
启动虚拟机,并配置512MiB的持久化CXL内存: start_qemu-pmem.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #!/bin/bash
QEMU_BIN="$HOME/tool/qemu/build/qemu-system-x86_64" KERNEL_IMG="$HOME/work/mempool/linux/arch/x86_64/boot/bzImage" DISK_IMG="$HOME/tool/qemu/qemu-cxl-img"
$QEMU_BIN \ -s \ -kernel $KERNEL_IMG \ -append "root=/dev/sda rw console=ttyS0,115200 ignore_loglevel nokaslr \ cxl_acpi.dyndbg=+fplm cxl_pci.dyndbg=+fplm cxl_core.dyndbg=+fplm \ cxl_mem.dyndbg=+fplm cxl_pmem.dyndbg=+fplm cxl_port.dyndbg=+fplm \ cxl_region.dyndbg=+fplm cxl_test.dyndbg=+fplm cxl_mock.dyndbg=+fplm \ cxl_mock_mem.dyndbg=+fplm dax.dyndbg=+fplm dax_cxl.dyndbg=+fplm \ device_dax.dyndbg=+fplm" \ -smp 1 \ -accel kvm \ -serial mon:stdio \ -nographic \ -qmp tcp:localhost:4444,server,wait=off \ -netdev user,id=network0,hostfwd=tcp::2024-:22 \ -device e1000,netdev=network0 \ -monitor telnet:127.0.0.1:12345,server,nowait \ -drive file=$DISK_IMG,index=0,media=disk,format=raw \ -machine q35,cxl=on -m 8G,maxmem=32G,slots=8 \ -virtfs local,path=/lib/modules,mount_tag=modshare,security_model=mapped \ -virtfs local,path=/home/zmy,mount_tag=homeshare,security_model=mapped -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest.raw,size=512M \ -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa.raw,size=512M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ -device cxl-type3,bus=root_port13,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k
|
启动虚拟机,并配置CXL动态容量设备: 2个区域,每个2GiB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #!/bin/bash
QEMU_BIN="$HOME/tool/qemu/build/qemu-system-x86_64" KERNEL_IMG="$HOME/work/mempool/linux/arch/x86_64/boot/bzImage" DISK_IMG="$HOME/tool/qemu/qemu-cxl-img"
$QEMU_BIN \ -s \ -kernel $KERNEL_IMG \ -append "root=/dev/sda rw console=ttyS0,115200 ignore_loglevel nokaslr \ cxl_acpi.dyndbg=+fplm cxl_pci.dyndbg=+fplm cxl_core.dyndbg=+fplm \ cxl_mem.dyndbg=+fplm cxl_pmem.dyndbg=+fplm cxl_port.dyndbg=+fplm \ cxl_region.dyndbg=+fplm cxl_test.dyndbg=+fplm cxl_mock.dyndbg=+fplm \ cxl_mock_mem.dyndbg=+fplm dax.dyndbg=+fplm dax_cxl.dyndbg=+fplm \ device_dax.dyndbg=+fplm" \ -smp 1 \ -accel kvm \ -serial mon:stdio \ -nographic \ -qmp tcp:localhost:4444,server,wait=off \ -netdev user,id=network0,hostfwd=tcp::2024-:22 \ -device e1000,netdev=network0 \ -monitor telnet:127.0.0.1:12345,server,nowait \ -drive file=$DISK_IMG,index=0,media=disk,format=raw \ -machine q35,cxl=on -m 8G,maxmem=32G,slots=8 \ -virtfs local,path=/lib/modules,mount_tag=modshare,security_model=mapped \ -virtfs local,path=/home/zmy,mount_tag=homeshare,security_model=mapped -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=13,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ -object memory-backend-file,id=dhmem0,share=on,mem-path=/tmp/dhmem0.raw,size=4G \ -object memory-backend-file,id=lsa0,share=on,mem-path=/tmp/lsa0.raw,size=512M \ -device cxl-type3,bus=root_port13,volatile-dc-memdev=dhmem0,num-dc-regions=2,id=cxl-memdev0 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8K
|
启动虚拟机网络:
1 2
| ip link set dev enp0s2 up dhclient enp0s2
|
查询CXL设备:
1 2 3 4 5
| apt install pciutils lspci lspci | grep CXL lspci -vvv lspci -vvv -s 0d:00.0
|
小结
按照上述流程,可以成功编译QEMU和Kernel,并使用虚拟机模拟CXL设备,美中不足的是参考文献的作者使用的是2.X的CXL设备,如何启动3.X的设备之后再探索
参考文献
- CXL-Emulator-QEMU