From c46e38be7e53867b957d8604081682b94e078e24 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Fri, 19 Dec 2025 12:20:29 -0800 Subject: [PATCH 1/5] WSLA: Switch to using PMEM for readonly VHDs that are added at boot --- src/windows/common/hcs_schema.h | 49 ++++++++++++ .../wslaservice/exe/WSLAVirtualMachine.cpp | 80 ++++++++++--------- .../wslaservice/exe/WSLAVirtualMachine.h | 2 +- 3 files changed, 94 insertions(+), 37 deletions(-) diff --git a/src/windows/common/hcs_schema.h b/src/windows/common/hcs_schema.h index 63d9ba74d..f2fab0303 100644 --- a/src/windows/common/hcs_schema.h +++ b/src/windows/common/hcs_schema.h @@ -458,6 +458,53 @@ inline void to_json(nlohmann::json& j, const DebugOptions& d) OMIT_IF_EMPTY(j, d, ShutdownOrResetSavedStateFileName); } +enum VirtualPMemImageFormat +{ + Vhdx, + Vhd1 +}; + +NLOHMANN_JSON_SERIALIZE_ENUM( + VirtualPMemImageFormat, + { + {VirtualPMemImageFormat::Vhdx, "Vhdx"}, + {VirtualPMemImageFormat::Vhd1, "Vhd1"}, + }) + +struct VirtualPMemDevice +{ + std::wstring HostPath; + bool ReadOnly; + VirtualPMemImageFormat ImageFormat; + // uint64_t SizeBytes; + // std::map Mappings; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(VirtualPMemDevice, HostPath, ReadOnly, ImageFormat); +}; + +enum class VirtualPMemBackingType +{ + Virtual, + Physical +}; + +NLOHMANN_JSON_SERIALIZE_ENUM( + VirtualPMemBackingType, + { + {VirtualPMemBackingType::Virtual, "Virtual"}, + {VirtualPMemBackingType::Physical, "Physical"}, + }) + +struct VirtualPMemController +{ + std::map Devices; + uint8_t MaximumCount; + uint64_t MaximumSizeBytes; + VirtualPMemBackingType Backing; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(VirtualPMemController, Devices, MaximumCount, MaximumSizeBytes, Backing); +}; + struct Devices { std::optional VirtioSerial; @@ -466,6 +513,7 @@ struct Devices EmptyObject Battery; HvSocket HvSocket; std::map Scsi; + std::optional VirtualPMem; }; inline void to_json(nlohmann::json& j, const Devices& devices) @@ -478,6 +526,7 @@ inline void to_json(nlohmann::json& j, const Devices& devices) {"Scsi", devices.Scsi}}; OMIT_IF_EMPTY(j, devices, VirtioSerial); + OMIT_IF_EMPTY(j, devices, VirtualPMem); } struct VirtualMachine diff --git a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp index fc05a326b..dcc63c4d6 100644 --- a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp +++ b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp @@ -282,17 +282,45 @@ void WSLAVirtualMachine::Start() vmSettings.Chipset.Uefi = std::move(uefiSettings); } - // Initialize other devices. + // Initialize SCSI controller. vmSettings.Devices.Scsi["0"] = hcs::Scsi{}; - hcs::HvSocket hvSocketConfig{}; + + // Initialize PMEM devices. + hcs::VirtualPMemController pmemController; + pmemController.MaximumCount = 0; + pmemController.MaximumSizeBytes = 0; + pmemController.Backing = hcs::VirtualPMemBackingType::Virtual; + auto attachPmemDisk = [&](PCWSTR path) { + ULONG deviceId = pmemController.MaximumCount; + pmemController.MaximumCount += 1; + hcs::VirtualPMemDevice vhd; + vhd.HostPath = path; + vhd.ReadOnly = true; + vhd.ImageFormat = hcs::VirtualPMemImageFormat::Vhd1; + pmemController.Devices[std::to_string(deviceId)] = std::move(vhd); + return std::format("/dev/pmem{}", deviceId); + }; + +#ifdef WSL_KERNEL_MODULES_PATH + + auto kernelModulesPath = std::filesystem::path(TEXT(WSL_KERNEL_MODULES_PATH)); + +#else + + auto kernelModulesPath = basePath / L"tools" / L"modules.vhd"; + +#endif + + const auto rootVhdDevicePath = attachPmemDisk(m_settings.RootVhd.c_str()); + const auto modulesDevicePath = attachPmemDisk(kernelModulesPath.c_str()); + vmSettings.Devices.VirtualPMem = std::move(pmemController); // Construct a security descriptor that allows system and the current user. wil::unique_hlocal_string userSidString; THROW_LAST_ERROR_IF(!ConvertSidToStringSidW(m_userSid, &userSidString)); - std::wstring securityDescriptor{L"D:P(A;;FA;;;SY)(A;;FA;;;"}; - securityDescriptor += userSidString.get(); - securityDescriptor += L")"; + std::wstring securityDescriptor = std::format(L"D:P(A;;FA;;;SY)(A;;FA;;;{})", userSidString.get()); + hcs::HvSocket hvSocketConfig{}; hvSocketConfig.HvSocketConfig.DefaultBindSecurityDescriptor = securityDescriptor; hvSocketConfig.HvSocketConfig.DefaultConnectSecurityDescriptor = securityDescriptor; vmSettings.Devices.HvSocket = std::move(hvSocketConfig); @@ -352,20 +380,18 @@ void WSLAVirtualMachine::Start() ConfigureNetworking(); - // Mount the kernel modules VHD. - -#ifdef WSL_KERNEL_MODULES_PATH - - auto kernelModulesPath = std::filesystem::path(TEXT(WSL_KERNEL_MODULES_PATH)); - -#else - - auto kernelModulesPath = basePath / L"tools" / L"modules.vhd"; - -#endif + ConfigureMounts(rootVhdDevicePath.c_str(), modulesDevicePath.c_str()); +} - auto [_, device] = AttachDisk(kernelModulesPath.c_str(), true); - Mount(m_initChannel, device.c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules); +void WSLAVirtualMachine::ConfigureMounts(LPCSTR RootVhdDevicePath, LPCSTR ModulesDevicePath) +{ + Mount(m_initChannel, RootVhdDevicePath, "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); + Mount(m_initChannel, nullptr, "/dev", "devtmpfs", "", 0); + Mount(m_initChannel, nullptr, "/sys", "sysfs", "", 0); + Mount(m_initChannel, nullptr, "/proc", "proc", "", 0); + Mount(m_initChannel, nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0); + Mount(m_initChannel, nullptr, "/sys/fs/cgroup", "cgroup2", "", 0); + Mount(m_initChannel, ModulesDevicePath, "", "ext4", "ro", WSLA_MOUNT::KernelModules); // Configure GPU if requested. if (FeatureEnabled(WslaFeatureFlagsGPU)) @@ -382,24 +408,6 @@ void WSLAVirtualMachine::Start() } wsl::windows::common::hcs::ModifyComputeSystem(m_computeSystem.get(), wsl::shared::ToJsonW(gpuRequest).c_str()); - } - - ConfigureMounts(); -} - -void WSLAVirtualMachine::ConfigureMounts() -{ - auto [_, device] = AttachDisk(m_settings.RootVhd.c_str(), true); - - Mount(m_initChannel, device.c_str(), "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); - Mount(m_initChannel, nullptr, "/dev", "devtmpfs", "", 0); - Mount(m_initChannel, nullptr, "/sys", "sysfs", "", 0); - Mount(m_initChannel, nullptr, "/proc", "proc", "", 0); - Mount(m_initChannel, nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0); - Mount(m_initChannel, nullptr, "/sys/fs/cgroup", "cgroup2", "", 0); - - if (FeatureEnabled(WslaFeatureFlagsGPU)) // TODO: re-think how GPU settings should work at the session level API. - { MountGpuLibraries("/usr/lib/wsl/lib", "/usr/lib/wsl/drivers"); } } diff --git a/src/windows/wslaservice/exe/WSLAVirtualMachine.h b/src/windows/wslaservice/exe/WSLAVirtualMachine.h index 192614b82..38a88e68e 100644 --- a/src/windows/wslaservice/exe/WSLAVirtualMachine.h +++ b/src/windows/wslaservice/exe/WSLAVirtualMachine.h @@ -106,7 +106,7 @@ class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine const WSLA_PROCESS_FD* Fds, ULONG FdCount, const WSLA_PROCESS_FD** TtyInput, const WSLA_PROCESS_FD** TtyOutput, const WSLA_PROCESS_FD** TtyControl); void ConfigureNetworking(); - void ConfigureMounts(); + void ConfigureMounts(LPCSTR RootVhdDevicePath, LPCSTR ModulesDevicePath); void OnExit(_In_ const HCS_EVENT* Event); void OnCrash(_In_ const HCS_EVENT* Event); bool FeatureEnabled(WSLAFeatureFlags Flag) const; From 99c4093b5235e2672f89cfb3dda3bea899ca6cb8 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Fri, 19 Dec 2025 14:16:06 -0800 Subject: [PATCH 2/5] enum -> enum class --- src/windows/common/hcs_schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/common/hcs_schema.h b/src/windows/common/hcs_schema.h index f2fab0303..4f6dbf0ff 100644 --- a/src/windows/common/hcs_schema.h +++ b/src/windows/common/hcs_schema.h @@ -458,7 +458,7 @@ inline void to_json(nlohmann::json& j, const DebugOptions& d) OMIT_IF_EMPTY(j, d, ShutdownOrResetSavedStateFileName); } -enum VirtualPMemImageFormat +enum class VirtualPMemImageFormat { Vhdx, Vhd1 From 49037af79e2bc75adf03f34de3277c8a868f1270 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Fri, 19 Dec 2025 16:33:08 -0800 Subject: [PATCH 3/5] add support for SCSI and PMEM at boot --- src/windows/common/WslClient.cpp | 2 + .../wslaservice/exe/WSLAVirtualMachine.cpp | 116 ++++++++++++------ .../wslaservice/exe/WSLAVirtualMachine.h | 2 +- src/windows/wslaservice/inc/wslaservice.idl | 1 + 4 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index 7fbfc72d8..5ca648fe8 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -1552,6 +1552,8 @@ int WslaShell(_In_ std::wstring_view commandLine) parser.AddArgument(Utf8String(shell), L"--shell"); parser.AddArgument( SetFlag(reinterpret_cast(sessionSettings.FeatureFlags)), L"--dns-tunneling"); + parser.AddArgument( + SetFlag(reinterpret_cast(sessionSettings.FeatureFlags)), L"--pmem-vhds"); parser.AddArgument( SetFlag(reinterpret_cast(sessionSettings.FeatureFlags)), L"--virtiofs"); parser.AddArgument(Integer(sessionSettings.MemoryMb), L"--memory"); diff --git a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp index dcc63c4d6..4f822e1b5 100644 --- a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp +++ b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp @@ -282,25 +282,6 @@ void WSLAVirtualMachine::Start() vmSettings.Chipset.Uefi = std::move(uefiSettings); } - // Initialize SCSI controller. - vmSettings.Devices.Scsi["0"] = hcs::Scsi{}; - - // Initialize PMEM devices. - hcs::VirtualPMemController pmemController; - pmemController.MaximumCount = 0; - pmemController.MaximumSizeBytes = 0; - pmemController.Backing = hcs::VirtualPMemBackingType::Virtual; - auto attachPmemDisk = [&](PCWSTR path) { - ULONG deviceId = pmemController.MaximumCount; - pmemController.MaximumCount += 1; - hcs::VirtualPMemDevice vhd; - vhd.HostPath = path; - vhd.ReadOnly = true; - vhd.ImageFormat = hcs::VirtualPMemImageFormat::Vhd1; - pmemController.Devices[std::to_string(deviceId)] = std::move(vhd); - return std::format("/dev/pmem{}", deviceId); - }; - #ifdef WSL_KERNEL_MODULES_PATH auto kernelModulesPath = std::filesystem::path(TEXT(WSL_KERNEL_MODULES_PATH)); @@ -311,9 +292,57 @@ void WSLAVirtualMachine::Start() #endif - const auto rootVhdDevicePath = attachPmemDisk(m_settings.RootVhd.c_str()); - const auto modulesDevicePath = attachPmemDisk(kernelModulesPath.c_str()); - vmSettings.Devices.VirtualPMem = std::move(pmemController); + // Initialize the boot VHDs. + std::pair, std::optional> rootVhd; + std::pair, std::optional> modulesVhd; + hcs::Scsi scsiController{}; + if (!FeatureEnabled(WslaFeatureFlagsPmemVhds)) + { + ULONG nextLun = 0; + auto attachScsiDisk = [&](PCWSTR path) { + auto lun = nextLun; + nextLun += 1; + hcs::Attachment disk{}; + disk.Type = hcs::AttachmentType::VirtualDisk; + disk.Path = path; + disk.ReadOnly = true; + disk.SupportCompressedVolumes = true; + disk.AlwaysAllowSparseFiles = true; + disk.SupportEncryptedFiles = true; + scsiController.Attachments[std::to_string(lun)] = std::move(disk); + AttachedDisk attachedDisk{path}; + // TODO: devicePath will be filled in later. + m_attachedDisks.emplace(lun, std::move(attachedDisk)); + return lun; + }; + + rootVhd.first = attachScsiDisk(m_settings.RootVhd.c_str()); + modulesVhd.first = attachScsiDisk(kernelModulesPath.c_str()); + } + else + { + hcs::VirtualPMemController pmemController; + pmemController.MaximumCount = 0; + pmemController.MaximumSizeBytes = 0; + pmemController.Backing = hcs::VirtualPMemBackingType::Virtual; + auto attachPmemDisk = [&](PCWSTR path) { + ULONG deviceId = pmemController.MaximumCount; + pmemController.MaximumCount += 1; + hcs::VirtualPMemDevice vhd; + vhd.HostPath = path; + vhd.ReadOnly = true; + vhd.ImageFormat = hcs::VirtualPMemImageFormat::Vhd1; + pmemController.Devices[std::to_string(deviceId)] = std::move(vhd); + return std::format("/dev/pmem{}", deviceId); + }; + + rootVhd.second = attachPmemDisk(m_settings.RootVhd.c_str()); + modulesVhd.second = attachPmemDisk(kernelModulesPath.c_str()); + vmSettings.Devices.VirtualPMem = std::move(pmemController); + } + + // Initialize the SCSI controller. + vmSettings.Devices.Scsi["0"] = std::move(scsiController); // Construct a security descriptor that allows system and the current user. wil::unique_hlocal_string userSidString; @@ -380,18 +409,26 @@ void WSLAVirtualMachine::Start() ConfigureNetworking(); - ConfigureMounts(rootVhdDevicePath.c_str(), modulesDevicePath.c_str()); -} + // Configure mounts. + auto getVhdDevicePath = [&](const std::pair, std::optional>& vhd) { + WI_ASSERT(vhd.first.has_value() ^ vhd.second.has_value()); + if (vhd.first.has_value()) + { + return GetVhdDevicePath(vhd.first.value()); + } + else + { + return vhd.second.value(); + } + }; -void WSLAVirtualMachine::ConfigureMounts(LPCSTR RootVhdDevicePath, LPCSTR ModulesDevicePath) -{ - Mount(m_initChannel, RootVhdDevicePath, "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); + Mount(m_initChannel, getVhdDevicePath(rootVhd).c_str(), "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); Mount(m_initChannel, nullptr, "/dev", "devtmpfs", "", 0); Mount(m_initChannel, nullptr, "/sys", "sysfs", "", 0); Mount(m_initChannel, nullptr, "/proc", "proc", "", 0); Mount(m_initChannel, nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0); Mount(m_initChannel, nullptr, "/sys/fs/cgroup", "cgroup2", "", 0); - Mount(m_initChannel, ModulesDevicePath, "", "ext4", "ro", WSLA_MOUNT::KernelModules); + Mount(m_initChannel, getVhdDevicePath(modulesVhd).c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules); // Configure GPU if requested. if (FeatureEnabled(WslaFeatureFlagsGPU)) @@ -688,17 +725,10 @@ std::pair WSLAVirtualMachine::AttachDisk(_In_ PCWSTR Path, _ vhdAdded = true; - WSLA_GET_DISK message{}; - message.Header.MessageSize = sizeof(message); - message.Header.MessageType = WSLA_GET_DISK::Type; - message.ScsiLun = Lun; - const auto& response = m_initChannel.Transaction(message); - - THROW_HR_IF_MSG(E_FAIL, response.Result != 0, "Failed to attach disk, init returned: %lu", response.Result); - + const auto devicePath = GetVhdDevicePath(Lun); cleanup.release(); - disk.Device = response.Buffer; + disk.Device = std::move(devicePath); Device = disk.Device; m_attachedDisks.emplace(Lun, std::move(disk)); }); @@ -809,6 +839,18 @@ WSLAVirtualMachine::ConnectedSocket WSLAVirtualMachine::ConnectSocket(wsl::share return socket; } +std::string WSLAVirtualMachine::GetVhdDevicePath(ULONG Lun) +{ + WSLA_GET_DISK message{}; + message.Header.MessageSize = sizeof(message); + message.Header.MessageType = WSLA_GET_DISK::Type; + message.ScsiLun = Lun; + const auto& response = m_initChannel.Transaction(message); + THROW_HR_IF_MSG(E_FAIL, response.Result != 0, "Failed to get disk path, init returned: %lu", response.Result); + + return response.Buffer; +} + void WSLAVirtualMachine::OpenLinuxFile(wsl::shared::SocketChannel& Channel, const char* Path, uint32_t Flags, int32_t Fd) { static_assert(WSLAFdTypeLinuxFileInput == WslaOpenFlagsRead); diff --git a/src/windows/wslaservice/exe/WSLAVirtualMachine.h b/src/windows/wslaservice/exe/WSLAVirtualMachine.h index 38a88e68e..7410c1a6e 100644 --- a/src/windows/wslaservice/exe/WSLAVirtualMachine.h +++ b/src/windows/wslaservice/exe/WSLAVirtualMachine.h @@ -106,7 +106,6 @@ class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine const WSLA_PROCESS_FD* Fds, ULONG FdCount, const WSLA_PROCESS_FD** TtyInput, const WSLA_PROCESS_FD** TtyOutput, const WSLA_PROCESS_FD** TtyControl); void ConfigureNetworking(); - void ConfigureMounts(LPCSTR RootVhdDevicePath, LPCSTR ModulesDevicePath); void OnExit(_In_ const HCS_EVENT* Event); void OnCrash(_In_ const HCS_EVENT* Event); bool FeatureEnabled(WSLAFeatureFlags Flag) const; @@ -117,6 +116,7 @@ class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine int32_t ExpectClosedChannelOrError(wsl::shared::SocketChannel& Channel); ConnectedSocket ConnectSocket(wsl::shared::SocketChannel& Channel, int32_t Fd); + std::string GetVhdDevicePath(ULONG Lun); static void OpenLinuxFile(wsl::shared::SocketChannel& Channel, const char* Path, uint32_t Flags, int32_t Fd); void LaunchPortRelay(); void RemoveShare(_In_ const MountedFolderInfo& MountInfo); diff --git a/src/windows/wslaservice/inc/wslaservice.idl b/src/windows/wslaservice/inc/wslaservice.idl index e89a8fc1b..e67b6c0d8 100644 --- a/src/windows/wslaservice/inc/wslaservice.idl +++ b/src/windows/wslaservice/inc/wslaservice.idl @@ -248,6 +248,7 @@ typedef enum _WSLAFeatureFlags WslaFeatureFlagsEarlyBootDmesg = 2, WslaFeatureFlagsGPU = 4, WslaFeatureFlagsVirtioFs = 8, + WslaFeatureFlagsPmemVhds = 16, } WSLAFeatureFlags; struct WSLA_SESSION_SETTINGS { From 1eddc1c34ff49e714f0d6be4d9ba7af987bf4322 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Wed, 31 Dec 2025 11:28:33 -0800 Subject: [PATCH 4/5] switch to std::variant --- .../wslaservice/exe/WSLAVirtualMachine.cpp | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp index 4f822e1b5..453b18bf5 100644 --- a/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp +++ b/src/windows/wslaservice/exe/WSLAVirtualMachine.cpp @@ -293,8 +293,8 @@ void WSLAVirtualMachine::Start() #endif // Initialize the boot VHDs. - std::pair, std::optional> rootVhd; - std::pair, std::optional> modulesVhd; + std::variant rootVhd; + std::variant modulesVhd; hcs::Scsi scsiController{}; if (!FeatureEnabled(WslaFeatureFlagsPmemVhds)) { @@ -311,13 +311,12 @@ void WSLAVirtualMachine::Start() disk.SupportEncryptedFiles = true; scsiController.Attachments[std::to_string(lun)] = std::move(disk); AttachedDisk attachedDisk{path}; - // TODO: devicePath will be filled in later. m_attachedDisks.emplace(lun, std::move(attachedDisk)); return lun; }; - rootVhd.first = attachScsiDisk(m_settings.RootVhd.c_str()); - modulesVhd.first = attachScsiDisk(kernelModulesPath.c_str()); + rootVhd = attachScsiDisk(m_settings.RootVhd.c_str()); + modulesVhd = attachScsiDisk(kernelModulesPath.c_str()); } else { @@ -336,8 +335,8 @@ void WSLAVirtualMachine::Start() return std::format("/dev/pmem{}", deviceId); }; - rootVhd.second = attachPmemDisk(m_settings.RootVhd.c_str()); - modulesVhd.second = attachPmemDisk(kernelModulesPath.c_str()); + rootVhd = attachPmemDisk(m_settings.RootVhd.c_str()); + modulesVhd = attachPmemDisk(kernelModulesPath.c_str()); vmSettings.Devices.VirtualPMem = std::move(pmemController); } @@ -410,25 +409,28 @@ void WSLAVirtualMachine::Start() ConfigureNetworking(); // Configure mounts. - auto getVhdDevicePath = [&](const std::pair, std::optional>& vhd) { - WI_ASSERT(vhd.first.has_value() ^ vhd.second.has_value()); - if (vhd.first.has_value()) + auto getDevicePath = [&](std::variant& vhd) -> const std::string& { + // If the variant holds the SCSI LUN, query the guest for the device path. + if (std::holds_alternative(vhd)) { - return GetVhdDevicePath(vhd.first.value()); - } - else - { - return vhd.second.value(); + const auto lun = std::get(vhd); + auto it = m_attachedDisks.find(lun); + WI_ASSERT(it != m_attachedDisks.end() && it->second.Device.empty()); + + it->second.Device = GetVhdDevicePath(lun); + vhd = it->second.Device; } + + return std::get(vhd); }; - Mount(m_initChannel, getVhdDevicePath(rootVhd).c_str(), "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); + Mount(m_initChannel, getDevicePath(rootVhd).c_str(), "/mnt", m_settings.RootVhdType.c_str(), "ro", WSLAMountFlagsChroot | WSLAMountFlagsWriteableOverlayFs); Mount(m_initChannel, nullptr, "/dev", "devtmpfs", "", 0); Mount(m_initChannel, nullptr, "/sys", "sysfs", "", 0); Mount(m_initChannel, nullptr, "/proc", "proc", "", 0); Mount(m_initChannel, nullptr, "/dev/pts", "devpts", "noatime,nosuid,noexec,gid=5,mode=620", 0); Mount(m_initChannel, nullptr, "/sys/fs/cgroup", "cgroup2", "", 0); - Mount(m_initChannel, getVhdDevicePath(modulesVhd).c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules); + Mount(m_initChannel, getDevicePath(modulesVhd).c_str(), "", "ext4", "ro", WSLA_MOUNT::KernelModules); // Configure GPU if requested. if (FeatureEnabled(WslaFeatureFlagsGPU)) From c75d01edd658c601e8c36f8285d33b4f03166ee4 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 6 Jan 2026 09:35:16 -0800 Subject: [PATCH 5/5] add tests for pmem vhds --- test/windows/WSLATests.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/windows/WSLATests.cpp b/test/windows/WSLATests.cpp index 38dfbc395..397b72264 100644 --- a/test/windows/WSLATests.cpp +++ b/test/windows/WSLATests.cpp @@ -971,6 +971,43 @@ class WSLATests ExpectCommandResult(session.get(), {"/bin/sh", "-c", "lsmod | grep ^xsk_diag"}, 0); } + TEST_METHOD(PmemVhds) + { + WSL2_TEST_ONLY(); + + // Test with SCSI boot VHDs. + { + auto settings = GetDefaultSessionSettings(); + WI_ClearFlag(settings.FeatureFlags, WslaFeatureFlagsPmemVhds); + + auto session = CreateSession(settings); + + // Validate that SCSI devices are present and PMEM devices are not. + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/sda"}, 0); + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/sdb"}, 0); + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/pmem0"}, 1); + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/pmem1"}, 1); + + // Verify that the SCSI device is readable. + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "dd if=/dev/sda of=/dev/null bs=512 count=1 2>&1"}, 0); + } + + // Test with PMEM boot VHDs enabled. + { + auto settings = GetDefaultSessionSettings(); + WI_SetFlag(settings.FeatureFlags, WslaFeatureFlagsPmemVhds); + + auto session = CreateSession(settings); + + // Validate that PMEM devices are present. + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/pmem0"}, 0); + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "test -b /dev/pmem1"}, 0); + + // Verify that the PMEM devices can be read from. + ExpectCommandResult(session.get(), {"/bin/sh", "-c", "dd if=/dev/pmem0 of=/dev/null bs=512 count=1 2>&1"}, 0); + } + } + TEST_METHOD(CreateRootNamespaceProcess) { WSL2_TEST_ONLY();