Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ find_nuget_package(Microsoft.RemoteDesktop.Client.MSRDC.SessionHost MSRDC /build
find_nuget_package(Microsoft.Taef TAEF /)
find_nuget_package(Microsoft.Windows.ImplementationLibrary WIL /)
find_nuget_package(Microsoft.WSL.DeviceHost WSL_DEVICE_HOST /build/native)

# Allow developers to swap in a locally-built wsldevicehost.dll without producing a new NuGet.
# Usage: cmake . -DWSL_DEVICE_HOST_DLL=C:/path/to/wsldevicehost.dll
set(WSL_DEVICE_HOST_DLL "" CACHE FILEPATH "Optional path to a wsldevicehost.dll that overrides the copy from the Microsoft.WSL.DeviceHost NuGet package.")
if (WSL_DEVICE_HOST_DLL)
if (NOT EXISTS "${WSL_DEVICE_HOST_DLL}")
message(FATAL_ERROR "WSL_DEVICE_HOST_DLL='${WSL_DEVICE_HOST_DLL}' does not exist.")
endif()
message(STATUS "Overriding wsldevicehost.dll with ${WSL_DEVICE_HOST_DLL}")
set(WSL_DEVICE_HOST_DLL_PATH "${WSL_DEVICE_HOST_DLL}")
else()
set(WSL_DEVICE_HOST_DLL_PATH "${WSL_DEVICE_HOST_SOURCE_DIR}/bin/${TARGET_PLATFORM}/wsldevicehost.dll")
endif()

find_nuget_package(Microsoft.WSL.Kernel KERNEL /build/native)
find_nuget_package(Microsoft.WSL.bsdtar BSDTARD /build/native/bin)
find_nuget_package(Microsoft.WSL.LinuxSdk LINUXSDK /)
Expand Down
6 changes: 6 additions & 0 deletions UserConfig.cmake.sample
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ endif()
# # error - block the commit when formatting issues are found
# # fix - automatically fix formatting and re-stage files
# set(WSL_PRE_COMMIT_MODE "warn")

# # Uncomment to replace the wsldevicehost.dll from the Microsoft.WSL.DeviceHost NuGet
# # package with a locally-built copy. The MSI build will pick up changes to the file
# # automatically, so you can iterate by rebuilding the dll and then running:
# # cmake --build . --target msipackage
# set(WSL_DEVICE_HOST_DLL "C:/path/to/wsldevicehost.dll")
1 change: 1 addition & 0 deletions doc/docs/dev-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Also see:

- `WSL_BUILD_THIN_PACKAGE` to build an even smaller package
- `WSL_POST_BUILD_COMMAND` to automatically deploy the package during build
- `WSL_DEVICE_HOST_DLL` to swap in a locally-built `wsldevicehost.dll` instead of the one from the `Microsoft.WSL.DeviceHost` NuGet package

**Code formatting**

Expand Down
12 changes: 12 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,10 @@ For more information on Admin Protection, please visit https://aka.ms/apdevguide
<value>Invalid IP value '{}' for key '{}' in {}:{}</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageConfigInvalidSwiotlb" xml:space="preserve">
<value>Invalid SWIOTLB value '{}' for key '{}' in {}:{}. Expected format: '0x&lt;hex&gt;,&lt;size&gt;K|M'</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageConfigInvalidBoolean" xml:space="preserve">
<value>Invalid boolean value '{}' for key '{}' in {}:{}</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
Expand Down Expand Up @@ -1126,6 +1130,14 @@ Attempting web download...</value>
<data name="MessageConfigVirtio9pDisabled" xml:space="preserve">
<value>wsl2.virtio9p is disabled, falling back to 9p with vsock transport.</value>
</data>
<data name="MessageVirtioProxyRequiresVirtio" xml:space="preserve">
<value>VirtioProxy networking is not supported when wsl2.virtio is disabled; falling back to networkingMode {}.</value>
<comment>{Locked="wsl2.virtio"}{Locked="VirtioProxy"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageSwiotlbKernelUnsupported" xml:space="preserve">
<value>The running kernel is missing a patch that significantly improves virtio device performance. Update to a more recent WSL kernel to enable this optimization.</value>
<comment>{Locked="virtio"}</comment>
</data>
<data name="MessageInstallationCorrupted" xml:space="preserve">
<value>WSL installation appears to be corrupted (Error code: {}).
Press any key to repair WSL, or CTRL-C to cancel.
Expand Down
4 changes: 4 additions & 0 deletions msipackage/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ foreach(binary ${LINUX_BINARIES})
list(APPEND BINARIES_DEPENDENCIES "${BIN}/${binary}")
endforeach()

# Track wsldevicehost.dll (either from the NuGet package or from a -DWSL_DEVICE_HOST_DLL override)
# so editing it triggers an MSI rebuild.
list(APPEND BINARIES_DEPENDENCIES "${WSL_DEVICE_HOST_DLL_PATH}")

if (${WSL_BUILD_THIN_PACKAGE})
set(COMPRESS_PACKAGE "no")
else()
Expand Down
2 changes: 1 addition & 1 deletion msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@
which would fail the install. -->
<ServiceControl Id="StopService" Stop="both" Remove="uninstall" Name="WSLService" Wait="yes" />

<File Id="wsldevicehost.dll" Source="${WSL_DEVICE_HOST_SOURCE_DIR}/bin/${TARGET_PLATFORM}/wsldevicehost.dll" />
<File Id="wsldevicehost.dll" Source="${WSL_DEVICE_HOST_DLL_PATH}" />

<!-- WSLC COM app - activated through WSLService -->
<RegistryKey Root="HKCR" Key="AppID\{E9B79997-57E3-4201-AECC-6A464E530DD2}">
Expand Down
4 changes: 2 additions & 2 deletions packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<package id="Microsoft.WSL.bsdtar" version="0.0.2-2" />
<package id="Microsoft.WSL.Dependencies.amd64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
<package id="Microsoft.WSL.Dependencies.arm64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
<package id="Microsoft.WSL.DeviceHost" version="1.2.23-0" />
<package id="Microsoft.WSL.Kernel" version="6.18.26.1-1" targetFramework="native" />
<package id="Microsoft.WSL.DeviceHost" version="1.2.29-0" />
<package id="Microsoft.WSL.Kernel" version="6.18.26.2-1" targetFramework="native" />
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
<package id="Microsoft.WSL.TestData" version="0.4.0" />
<package id="Microsoft.WSL.TestDistro" version="2.7.1-1" />
Expand Down
11 changes: 11 additions & 0 deletions src/linux/init/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ struct VmConfiguration
int g_LogFd = STDERR_FILENO;
int g_TelemetryFd = -1;
std::optional<bool> g_EnableSocketLogging;
bool g_KernelSupportsHvPciSwiotlb = false;

int Chroot(const char* Target);

Expand Down Expand Up @@ -3399,6 +3400,7 @@ try

uint32_t SeccompFlag = SECCOMP_RET_USER_NOTIF;
Message->SeccompAvailable = syscall(__NR_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, &SeccompFlag) == 0;
Message->KernelSupportsHvPciSwiotlb = g_KernelSupportsHvPciSwiotlb;

Channel.SendMessage<LX_INIT_GUEST_CAPABILITIES>(Message.Span());
return 0;
Expand Down Expand Up @@ -3694,6 +3696,15 @@ int main(int Argc, char* Argv[])
LOG_ERROR("unsetenv failed {}", errno);
}

// Linux passes unrecognized key=value cmdline parameters to init as env vars,
// so seeing hv_pci_swiotlb= in the environment means the kernel didn't consume
// it (no WSL swiotlb patch).
g_KernelSupportsHvPciSwiotlb = (getenv("hv_pci_swiotlb") == nullptr);
if (!g_KernelSupportsHvPciSwiotlb && unsetenv("hv_pci_swiotlb"))
{
LOG_ERROR("unsetenv failed {}", errno);
}

// Use an env variable to determine whether socket logging is enabled since /proc isn't mounted yet
// so SocketChannel can't look at the kernel command line.
wsl::shared::SocketChannel::EnableSocketLogging(getenv(WSL_SOCKET_LOG_ENV) != nullptr);
Expand Down
3 changes: 2 additions & 1 deletion src/shared/inc/lxinitshared.h
Original file line number Diff line number Diff line change
Expand Up @@ -1430,9 +1430,10 @@ typedef struct _LX_INIT_GUEST_CAPABILITIES

MESSAGE_HEADER Header;
bool SeccompAvailable;
bool KernelSupportsHvPciSwiotlb;
char Buffer[]; // Contains the kernel version string

PRETTY_PRINT(FIELD(Header), FIELD(SeccompAvailable), BUFFER_FIELD(Buffer));
PRETTY_PRINT(FIELD(Header), FIELD(SeccompAvailable), FIELD(KernelSupportsHvPciSwiotlb), BUFFER_FIELD(Buffer));
} LX_INIT_GUEST_CAPABILITIES, *PLX_INIT_GUEST_CAPABILITIES;

typedef struct _LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE
Expand Down
14 changes: 14 additions & 0 deletions src/windows/common/DeviceHostProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ void DeviceHostProxy::Shutdown()
{
{
auto lock = m_lock.lock_exclusive();

for (auto& entry : m_fileSystems)
{
try
{
wil::com_ptr<IPlan9FileSystem> instance;
THROW_IF_FAILED(
m_git->GetInterfaceFromGlobal(entry.Cookie, __uuidof(IPlan9FileSystem), reinterpret_cast<void**>(instance.put())));

LOG_IF_FAILED(instance->Teardown());
}
CATCH_LOG()
}

m_fileSystems.clear();
m_shutdown = true;
}
Expand Down
9 changes: 7 additions & 2 deletions src/windows/common/GuestDeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include "DeviceHostProxy.h"

GuestDeviceManager::GuestDeviceManager(_In_ const std::wstring& machineId, _In_ const GUID& runtimeId) :
m_machineId(machineId), m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId))
m_machineId(machineId),
m_vmIdOption(std::format(L";vm_id={}", machineId)),
m_deviceHostSupport(wil::MakeOrThrow<DeviceHostProxy>(machineId, runtimeId))
{
}

Expand Down Expand Up @@ -35,13 +37,16 @@ GUID GuestDeviceManager::AddHdvShareWithOptions(
// Options are appended to the name with a semi-colon separator.
// "name;key1=value1;key2=value2"
// The AddSharePath implementation is responsible for separating them out and interpreting them.
// N.B. A ";vm_id=<guid>" option is always appended so the device host can identify the owning VM.
std::wstring nameWithOptions{AccessName};
if (ARGUMENT_PRESENT(Options))
if (ARGUMENT_PRESENT(Options) && Options[0] != L'\0')
{
nameWithOptions += L";";
nameWithOptions += Options;
}

nameWithOptions += m_vmIdOption;

{
auto revert = wil::impersonate_token(UserToken);

Expand Down
1 change: 1 addition & 0 deletions src/windows/common/GuestDeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class GuestDeviceManager

wil::srwlock m_lock;
std::wstring m_machineId;
std::wstring m_vmIdOption;
wil::com_ptr<DeviceHostProxy> m_deviceHostSupport;
_Guarded_by_(m_lock) std::vector<DirectoryObjectLifetime> m_objectDirectories;
};
26 changes: 21 additions & 5 deletions src/windows/common/VirtioNetworking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ static constexpr auto c_eth0DeviceName = L"eth0";
static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);

VirtioNetworking::VirtioNetworking(
GnsChannel&& gnsChannel, VirtioNetworkingFlags flags, LPCWSTR dnsOptions, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken) :
GnsChannel&& gnsChannel,
VirtioNetworkingFlags flags,
LPCWSTR dnsOptions,
std::shared_ptr<GuestDeviceManager> guestDeviceManager,
wil::shared_handle userToken,
std::wstring swiotlbConfig) :
m_guestDeviceManager(std::move(guestDeviceManager)),
m_userToken(std::move(userToken)),
m_gnsChannel(std::move(gnsChannel)),
m_flags(flags),
m_dnsOptions(dnsOptions)
m_dnsOptions(dnsOptions),
m_swiotlbConfig(std::move(swiotlbConfig))
{
}

Expand Down Expand Up @@ -209,8 +215,15 @@ void VirtioNetworking::RefreshGuestConnection()
{
if (!m_adapterId.has_value())
{
const auto swiotlbOption = m_swiotlbConfig.empty() ? std::wstring{} : std::format(L"swiotlb={}", m_swiotlbConfig);
m_adapterId = m_guestDeviceManager->AddGuestDevice(
VIRTIO_NET_DEVICE_ID, VIRTIO_NET_CLASS_ID, c_eth0DeviceName, nullptr, device_options.c_str(), 0, m_userToken.get());
VIRTIO_NET_DEVICE_ID,
VIRTIO_NET_CLASS_ID,
c_eth0DeviceName,
swiotlbOption.c_str(),
device_options.c_str(),
0,
m_userToken.get());
}
else
{
Expand Down Expand Up @@ -240,12 +253,15 @@ void VirtioNetworking::RefreshGuestConnection()

void VirtioNetworking::SetupLoopbackDevice()
{
std::wstring loopbackOptions = L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55";
const auto swiotlbOption = m_swiotlbConfig.empty() ? std::wstring{} : std::format(L"swiotlb={}", m_swiotlbConfig);

m_localhostAdapterId = m_guestDeviceManager->AddGuestDevice(
VIRTIO_NET_DEVICE_ID,
VIRTIO_NET_CLASS_ID,
c_loopbackDeviceName,
nullptr,
L"client_ip=127.0.0.1;client_mac=00:11:22:33:44:55",
swiotlbOption.c_str(),
loopbackOptions.c_str(),
0,
m_userToken.get());

Expand Down
9 changes: 8 additions & 1 deletion src/windows/common/VirtioNetworking.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ DEFINE_ENUM_FLAG_OPERATORS(VirtioNetworkingFlags);
class VirtioNetworking : public INetworkingEngine
{
public:
VirtioNetworking(GnsChannel&& gnsChannel, VirtioNetworkingFlags flags, LPCWSTR dnsOptions, std::shared_ptr<GuestDeviceManager> guestDeviceManager, wil::shared_handle userToken);
VirtioNetworking(
GnsChannel&& gnsChannel,
VirtioNetworkingFlags flags,
LPCWSTR dnsOptions,
std::shared_ptr<GuestDeviceManager> guestDeviceManager,
wil::shared_handle userToken,
std::wstring swiotlbConfig);

~VirtioNetworking();

Expand Down Expand Up @@ -62,6 +68,7 @@ class VirtioNetworking : public INetworkingEngine
std::shared_ptr<networking::NetworkSettings> m_networkSettings;
VirtioNetworkingFlags m_flags = VirtioNetworkingFlags::None;
LPCWSTR m_dnsOptions = nullptr;
std::wstring m_swiotlbConfig;
std::optional<GUID> m_localhostAdapterId;
std::optional<GUID> m_adapterId;

Expand Down
69 changes: 52 additions & 17 deletions src/windows/common/WslCoreConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Module Name:

#include "precomp.h"
#include "WslCoreConfig.h"
#include "helpers.hpp"
#include "Localization.h"
#include "WslCoreFirewallSupport.h"
#include "WslCoreNetworkingSupport.h"
Expand Down Expand Up @@ -63,6 +64,24 @@ void wsl::core::Config::ParseConfigFile(_In_opt_ LPCWSTR ConfigFilePath, _In_opt
}
};

auto parseSwiotlb = [&](const char* name, const char* value, const wchar_t* fileName, unsigned long fileLine) {
// If the value does not conform to the expected format, SWIOTLB customization is disabled.
SwiotlbConfig.clear();
try
{
auto wideValue = wsl::shared::string::MultiByteToWide(value);
std::wregex swiotlbPattern(L"^(0x[0-9a-fA-F]+,[0-9]+[mk])$", std::regex::icase);
if (!std::regex_match(wideValue, swiotlbPattern))
{
EMIT_USER_WARNING(shared::Localization::MessageConfigInvalidSwiotlb(value, name, fileName, fileLine));
return;
}

SwiotlbConfig = std::move(wideValue);
}
CATCH_LOG()
};

ConfigKeyPresence earlyBootLoggingPresent{};
ConfigKeyPresence macAddressPresent{};
bool enableFirewall = true;
Expand Down Expand Up @@ -124,7 +143,8 @@ void wsl::core::Config::ParseConfigFile(_In_opt_ LPCWSTR ConfigFilePath, _In_opt
ConfigKey(ConfigSetting::Experimental::InitialAutoProxyTimeout, InitialAutoProxyTimeout),
ConfigKey(ConfigSetting::Experimental::IgnoredPorts, std::move(parseIgnoredPorts)),
ConfigKey(ConfigSetting::Experimental::HostAddressLoopback, EnableHostAddressLoopback),
ConfigKey(ConfigSetting::Experimental::SetVersionDebug, SetVersionDebug)};
ConfigKey(ConfigSetting::Experimental::SetVersionDebug, SetVersionDebug),
ConfigKey(ConfigSetting::Experimental::Swiotlb, std::move(parseSwiotlb))};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the thinking for exposing a new .wslconfig value for this ? Could we set a hardcoded value first ? That would greatly simplify this change


wil::unique_file ConfigFile;
if (ConfigFilePath != nullptr)
Expand Down Expand Up @@ -398,22 +418,6 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken)
}
}

// Load NAT configuration from the registry.
if (NetworkingMode == wsl::core::NetworkingMode::Nat)
{
try
{
const auto machineKey = wsl::windows::common::registry::OpenLxssMachineKey();
NatGateway = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natGatewayAddress, L"");
NatNetwork = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natNetwork, L"");

auto runAsUser = wil::impersonate_token(UserToken);
const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();
NatIpAddress = wsl::windows::common::registry::ReadString(userKey.get(), nullptr, c_natIpAddress, L"");
}
CATCH_LOG()
}

// Due to an issue with Global Secure Access Client, do not use DNS tunneling if the service is present.
if (EnableDnsTunneling)
{
Expand Down Expand Up @@ -454,6 +458,13 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken)
{
VALIDATE_CONFIG_OPTION(!EnableVirtio, EnableVirtio9p, false);
VALIDATE_CONFIG_OPTION(!EnableVirtio, EnableVirtioFs, false);
VALIDATE_CONFIG_OPTION(!EnableVirtio, SwiotlbConfig, std::wstring{});

if (NetworkingMode == NetworkingMode::VirtioProxy)
{
NetworkingMode = (defaultNetworkingMode == NetworkingMode::VirtioProxy) ? NetworkingMode::None : NetworkingMode::Nat;
EMIT_USER_WARNING(wsl::shared::Localization::MessageVirtioProxyRequiresVirtio(ToString(NetworkingMode)));
}
}

if (EnableVirtio9p)
Expand All @@ -462,6 +473,13 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken)
EnableVirtio9p = false;
}

// Compute a default swiotlb config only when a virtio device that requires bounce buffers is present.
// N.B. Must run after policy overrides so networking/fs modes reflect final values.
if (SwiotlbConfig.empty() && (EnableVirtioFs || EnableVirtio9p || (NetworkingMode == NetworkingMode::VirtioProxy)))
{
SwiotlbConfig = wsl::windows::common::helpers::ComputeDefaultSwiotlbConfig(MemorySizeBytes);
}

if (NetworkingMode != NetworkingMode::Nat && NetworkingMode != NetworkingMode::Mirrored && NetworkingMode != NetworkingMode::VirtioProxy)
{
VALIDATE_CONFIG_OPTION(
Expand All @@ -481,6 +499,23 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken)
VALIDATE_CONFIG_OPTION((NetworkingMode != NetworkingMode::Mirrored), IgnoredPorts, std::set<uint16_t>{});
VALIDATE_CONFIG_OPTION((NetworkingMode != NetworkingMode::Mirrored), EnableHostAddressLoopback, false);
}

// Load NAT configuration from the registry.
// N.B. This must be done after all networking mode adjustments (e.g. VirtioProxy -> NAT fallback).
if (NetworkingMode == wsl::core::NetworkingMode::Nat)
{
try
{
const auto machineKey = wsl::windows::common::registry::OpenLxssMachineKey();
NatGateway = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natGatewayAddress, L"");
NatNetwork = wsl::windows::common::registry::ReadString(machineKey.get(), nullptr, c_natNetwork, L"");

auto runAsUser = wil::impersonate_token(UserToken);
const auto userKey = wsl::windows::common::registry::OpenLxssUserKey();
NatIpAddress = wsl::windows::common::registry::ReadString(userKey.get(), nullptr, c_natIpAddress, L"");
}
CATCH_LOG()
}
}

GUID wsl::core::Config::NatNetworkId() const noexcept
Expand Down
Loading
Loading