使用命令:
egrep -c '(vmx|svm)' /proc/cpuinfo
如果返回0则说明不支持,大于零则支持。支持可以直接进行下一步。
若不支持首先查看是否开启了硬件虚拟化,在任务管理器->性能
中查看虚拟化是否已开启,如下图所示:
若没有开启则需要重启进入BIOS将Intel® Virtualization Technology选为Enabled、Intel® VT-d Feature选为Enabled。
当开启以上两个后,若WSL仍反映没有硬件虚拟化,则需要执行如下步骤,我们把这个步骤称为WinDbg步骤
,请记住这个,因为这意味着,当你的WSL被shutdown后你每次都要执行该步骤以确保WSL下CPU硬件虚拟化的开启:
在Microsoft shop下载这个:
下载后将以下两个文件放在同一个目录下(这里感谢Steffengy):
run-start.bat
REM Ensure vmcompute.exe is running
wsl.exe -e true
REM Listen for and Intercept utility vm creation
start Windbgx.exe -pn vmcompute.exe -c "bp vmcompute!Marshal::JsonParser::JsonParser;g;.scriptrun %CD%\script.js;.scriptrun %CD%\script.js;.scriptrun %CD%\script.js;.detach;qq"
REM Ensure WSL Utility VM is not running (hopefully windb starts up fast enough...)
net stop LxssManager
net start LxssManager
echo "Press Enter if the debugger is running"
pause
REM Start WSL
start wsl.exe
script.js
"use strict";
function initializeScript()
{
return [new host.apiVersionSupport(1, 3)];
}
function continueExecution() {
var cmd = "g";
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
}
function invokeScript()
{
/* bp vmcompute!Marshal::JsonParser::JsonParser */
var cmd;
var lines;
// 1. Check if WSL
var magic = host.memory.readWideString(host.currentThread.Registers.User.rdx, 14);
if (magic == '{"Owner":"WSL"') {
host.diagnostics.debugLog("IS WSL\n");
} else {
host.diagnostics.debugLog("IS NOT WSL request\n");
return continueExecution();
}
// dump length and read machine spec json
var len = host.currentThread.Registers.User.r8;
host.diagnostics.debugLog("String length: ", len, " dumping memory: ", host.currentThread.Registers.User.rdx, "\n");
var jsonString = host.memory.readWideString(host.currentThread.Registers.User.rdx, len);
host.diagnostics.debugLog("Before: ", jsonString, "\n");
// parse and modify machine spec json
var machineSpec = JSON.parse(jsonString);
machineSpec.VirtualMachine.ComputeTopology.Processor.ExposeVirtualizationExtensions = true;
// machineSpec.VirtualMachine.Chipset.LinuxKernelDirect.KernelFilePath = "D:\\temp\\vmlinux.bin";
var machineSpecJson = JSON.stringify(machineSpec);
host.diagnostics.debugLog("After: ", JSON.stringify(machineSpec), "\n");
var newLen = "0x" + machineSpecJson.length.toString(16);
// allocate memory
cmd = ".dvalloc 4096"
lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
var addr = lines[0];
var addrParts = addr.split(" ");
var freeMem = addrParts[addrParts.length-1].replace("`", "");
host.diagnostics.debugLog("Allocated ", freeMem, "\n");
// write memory
host.diagnostics.debugLog("Writing memory ", freeMem, " length: ", newLen, "\n");
cmd = "eu " + freeMem + ' "' + machineSpecJson.split("\\").join("\\\\").split('"').join('\\"') + '"';
lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
// patch rdx with new memory address
var cmd = "r @rdx = " + freeMem;
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
// patch r8 with new memory size
var cmd = "r @r8 = " + newLen;
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
return continueExecution();
}
接着在这两个文件所在的目录下以管理员
身份执行run-start.bat
文件,此时会跳出WinDbg.exe
执行,并且bat
文件会出现按任意键继续
的字样,此时按任意键,会弹出WSL
执行。此时若输入命令查看,则WSL的硬件CPU虚拟化被开启,即使用egrep -c '(vmx|svm)' /proc/cpuinfo
得到的值为正值,如下图所示:
由于直接从Microsoft Shop安装的WSL默认没有配置KVM模块,因此需要重新编译WSL的内核。
首先去GitHub上下载最新的内核文件:链接:
也可以直接使用如下命令下载:
cd ~
aria2c -x 10 https://github.com/microsoft/WSL2-Linux-Kernel/archive/4.19.128-microsoft-standard.tar.gz
下载后解压进入该目录下:
tar zxf WSL2-Linux-Kernel-4.19.128-microsoft-standard.tar.gz
cd WSL2-Linux-Kernel-4.19.128-microsoft-standard
进入WSL2-Linux-Kernel-4.19.128-microsoft-standard
目录后执行如下命令:
cp Microsoft/config-wsl .config
make menuconfig
此时会进入BIOS配置,进入Virtualization
:
在KVM support
上点击Y
打开kvm支持,并在KVM for Intel
上点击M
将其添加至模块(如果你不需要调整该模块,可以直接将其存入内核中,而不是以模块形式进行编译,这样避免了每次使用sudo modprobe kvm_intel
命令装载):
返回上一级后进入Processor
中:
然后进入Linux guest support
:
然后将KVM Guest support
打开:
最后退出并保存即可。退出后正式进行编译:
make -j4
如果你把KVM当作模块编译的话执行如下命令,若直接作为内核一部分则直接跳过:
sudo make modules_install
将arch/x86/boot/bzImage
移入C盘你的用户目录下:
cp arch/x86/boot/bzImage /mnt/c/Users/lyg/
然后在/mnt/c/Users/lyg
目录下创建配置文件.wslconfig
如下:
[wsl2]
nestedVirtualization=true
kernel=C:\\Users\\lyg\\bzImage
pageReporting=true
kernelCommandLine=intel_iommu=on iommu=pt kvm.ignore_msrs=1 kvm-intel.nested=1 kvm-intel.ept=1 kvm-intel.emulate_invalid_guest_state=0 kvm-intel.enable_shadow_vmcs=1 kvm-intel.enable_apicv=1
然后在CMD执行wsl --shutdown
将WSL关闭后再打开,此时使用uname -r
命令查看内核版本应该变为4.19.128
。
sudo vim /etc/modprobe.d/kvm-nested.conf
# 将如下配置信息放入上述文件即可
options kvm-intel nested=1
options kvm-intel enable_shadow_vmcs=1
options kvm-intel enable_apicv=1
options kvm-intel ept=1
如果你在执行make menuconfig
过程中中KVM for Intel
选择的是*
而不是M
,则忽略装载直接测试即可:
sudo modprobe kvm_intel
为保证每次启动WSL都能自动装载该模块,请把它写入~/.bashrc
中(但更好的方式时编译内核时直接将该模块固定在内核中),如下:
sudo modprobe kvm_intel
sudo chmod 666 /dev/kvm
如果你曾执行过WinDbg步骤
,则在测试前请再次执行后用下述命令测试。如果你没有执行过,则直接使用如下命令测试:
kvm-ok
cat /sys/module/kvm_intel/parameters/nested
至此完成安装,当然你可以使用qemu虚拟机进行测试,具体测试不赘述。笔者用syzkaller测试得到如下结果:
观察到成功运行qemu。
[1]. https://boxofcables.dev/accelerated-kvm-guests-on-wsl-2/
[2]. https://sugeul.github.io/2020-06-21-MacOS-on-WSL2/