Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
typedef enum WslcSessionFeatureFlags
{
WSLC_SESSION_FEATURE_FLAG_NONE = 0x00000000,
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU = 0x00000004
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU = 0x00000004,
WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION = 0x00000040
} WslcSessionFeatureFlags;
```

| Enumerator | Value |
|---|---|
| `WSLC_SESSION_FEATURE_FLAG_NONE` | `0x00000000` |
| `WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU` | `0x00000004` |
| `WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION` | `0x00000040` |
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ Example:
```c
HRESULT hr = WslcSetSessionSettingsFeatureFlags(
&sessionSettings,
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU);
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU |
WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION);
```
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Do not put credentials or other sensitive information in the session's name.
- `VhdRequirements()` / setter
- setter rejects `nullptr`
- `EnableGpu()` / setter
- `EnableNestedVirtualization()` / setter

```cpp
SessionSettings settings{ L"demo", L"C:\\WSLC\\demo" };
Expand All @@ -39,11 +40,13 @@ settings.MemorySizeInMB(winrt::box_value<uint32_t>(4096).as<winrt::Windows::Foun
settings.Timeout(winrt::box_value(winrt::Windows::Foundation::TimeSpan{ std::chrono::minutes(5) })
.as<winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::TimeSpan>>());
settings.EnableGpu(true);
settings.EnableNestedVirtualization(true);

auto name = settings.Name();
auto path = settings.StoragePath();
auto cpu = settings.CpuCount();
auto memory = settings.MemorySizeInMB();
auto timeout = settings.Timeout();
auto enableGpu = settings.EnableGpu();
auto enableNestedVirtualization = settings.EnableNestedVirtualization();
```
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public sealed class SessionSettings
public TimeSpan? Timeout { get; set; }
public VhdOptions VhdRequirements { get; set; }
public bool EnableGpu { get; set; }
public bool EnableNestedVirtualization { get; set; }
}
```

Expand Down Expand Up @@ -43,6 +44,7 @@ var sessionSettings = new SessionSettings("demo-session", @"C:\WslcData")
CpuCount = 4,
MemorySizeInMB = 4096,
Timeout = TimeSpan.FromMinutes(5),
EnableGpu = true
EnableGpu = true,
EnableNestedVirtualization = true
};
```
11 changes: 11 additions & 0 deletions intune/WSL.admx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@
</disabledValue>
</policy>

<policy name="AllowWSLContainerNestedVirtualization" class="Machine" displayName="$(string.AllowWSLContainerNestedVirtualization)" explainText="$(string.AllowWSLContainerNestedVirtualizationExplain)" key="Software\Policies\WSL" valueName="AllowWSLContainerNestedVirtualization">
<parentCategory ref="WSLContainer" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>

<policy name="WSLContainerRegistryAllowlist" class="Machine" displayName="$(string.WSLContainerRegistryAllowlist)" explainText="$(string.WSLContainerRegistryAllowlistExplain)" presentation="$(presentation.WSLContainerRegistryAllowlist)" key="Software\Policies\WSL">
<parentCategory ref="WSLContainer" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
Expand Down
3 changes: 3 additions & 0 deletions intune/en-US/WSL.adml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<string id="AllowWSLContainer"><!-- _locComment_text='{Locked="WSL"}' -->Allow WSL container</string>
<string id="AllowWSLContainerExplain"><!-- _locComment_text='{Locked="WSL"}{Locked="Disabled"}' -->This policy controls whether WSL container can be used on this machine. When enabled or not configured, users and Windows applications can run Linux containers via WSL. When set to disabled, WSL container is blocked for all users and Windows apps cannot run Linux containers. Warning: Setting this to 'Disabled' can break Windows apps that depend on Linux containers.</string>

<string id="AllowWSLContainerNestedVirtualization"><!-- _locComment_text='{Locked="WSL"}' -->Allow nested virtualization for WSL containers</string>
<string id="AllowWSLContainerNestedVirtualizationExplain"><!-- _locComment_text='{Locked="WSL"}{Locked="disabled"}' -->This policy controls whether WSL container sessions can enable nested virtualization. When enabled or not configured, applications can request nested virtualization for WSL container sessions. When set to disabled, requests to create WSL container sessions with nested virtualization enabled are rejected.</string>
Comment on lines +58 to +59

<string id="WSLContainerRegistryAllowlist"><!-- _locComment_text='{Locked="WSL"}' -->Allowlist for WSL container registries</string>
<string id="WSLContainerRegistryAllowlistExplain"><!-- _locComment_text='{Locked="WSL"}' -->When enabled, WSL container will only be allowed to pull images from the registries listed here. This affects both the WSL container CLI and all applications using the WSL container API.</string>
</stringTable>
Expand Down
3 changes: 3 additions & 0 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,9 @@ Falling back to NAT networking.</value>
<data name="MessageWSLContainerDisabled" xml:space="preserve">
<value>WSL container is disabled by the computer policy.</value>
</data>
<data name="MessageWSLContainerNestedVirtualizationDisabled" xml:space="preserve">
<value>Nested virtualization for WSL containers is disabled by the computer policy.</value>
</data>
<data name="MessageRegistryBlockedByPolicy" xml:space="preserve">
<value>The container image registry '{}' is blocked by the computer policy.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
Expand Down
101 changes: 83 additions & 18 deletions src/linux/init/WSLCInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ Module Name:
#include <sys/signalfd.h>
#include <arpa/inet.h>

#ifdef __x86_64__
#include <cpuid.h>
#endif

#include <array>
#include <cstring>
#include <pty.h>
#include <mutex>
#include "mountutilcpp.h"
Expand Down Expand Up @@ -669,6 +675,47 @@ void HandleMessageImpl(
Transaction.Send(Response);
}

void LoadKvmModule()
Comment thread
benhillis marked this conversation as resolved.
{
#ifdef __x86_64__

unsigned int eax{};
unsigned int ebx{};
unsigned int ecx{};
unsigned int edx{};
THROW_ERRNO_IF(ENOTSUP, __get_cpuid(0, &eax, &ebx, &ecx, &edx) == 0);

std::array<char, 13> vendor{};
memcpy(vendor.data(), &ebx, sizeof(ebx));
memcpy(vendor.data() + sizeof(ebx), &edx, sizeof(edx));
memcpy(vendor.data() + sizeof(ebx) + sizeof(edx), &ecx, sizeof(ecx));

const char* module = nullptr;
if (strcmp(vendor.data(), "GenuineIntel") == 0)
{
module = "kvm_intel";
}
else if (strcmp(vendor.data(), "AuthenticAMD") == 0)
{
module = "kvm_amd";
}

if (module == nullptr)
{
LOG_ERROR("Unsupported processor vendor for KVM: '{}'", vendor.data());
THROW_ERRNO(ENOTSUP);
}

const char* argv[] = {"/sbin/modprobe", module, nullptr};
THROW_ERRNO_IF(EIO, UtilCreateProcessAndWait("/sbin/modprobe", argv) < 0);

#else

THROW_ERRNO(ENOTSUP);

#endif
}

template <typename TMessage>
void HandleMountMessage(
wsl::shared::SocketChannel& Channel, wsl::shared::Transaction& Transaction, const TMessage& Message, const gsl::span<gsl::byte>& Buffer)
Expand Down Expand Up @@ -697,23 +744,7 @@ void HandleMountMessage(

const char* source = readField(Message.SourceIndex);

const char* target{};
if (WI_IsFlagSet(Message.Flags, WSLC_MOUNT::KernelModules))
{
assert(!g_state.ModulesMountPoint.has_value());

// Modules need to be mounted to a specific path that depends on the kernel version.

utsname UnameBuffer{};
THROW_LAST_ERROR_IF(uname(&UnameBuffer) < 0);

g_state.ModulesMountPoint = std::format("/lib/modules/{}", UnameBuffer.release);
target = g_state.ModulesMountPoint->c_str();
}
else
{
target = readField(Message.DestinationIndex);
}
const char* target = readField(Message.DestinationIndex);

// Chroot without OverlayFs is not supported — the chroot logic depends on the overlay target path.
THROW_ERRNO_IF(EINVAL, WI_IsFlagSet(Message.Flags, WSLC_MOUNT::Chroot) && !WI_IsFlagSet(Message.Flags, WSLC_MOUNT::OverlayFs));
Expand Down Expand Up @@ -842,6 +873,40 @@ void HandleMessageImpl(
HandleMountMessage(Channel, Transaction, Message, Buffer);
}

void HandleMessageImpl(
wsl::shared::SocketChannel& Channel, wsl::shared::Transaction& Transaction, const WSLC_MOUNT_MODULES& Message, const gsl::span<gsl::byte>& Buffer)
{
WSLC_MOUNT_RESULT response{};
response.Header.MessageType = WSLC_MOUNT_RESULT::Type;
response.Header.MessageSize = sizeof(response);

try
{
assert(!g_state.ModulesMountPoint.has_value());

utsname unameBuffer{};
THROW_LAST_ERROR_IF(uname(&unameBuffer) < 0);

g_state.ModulesMountPoint = std::format("/lib/modules/{}", unameBuffer.release);
const char* source = wsl::shared::string::FromSpan(Buffer, Message.SourceIndex);
THROW_LAST_ERROR_IF(UtilMount(source, g_state.ModulesMountPoint->c_str(), "ext4", MS_RDONLY, "", c_defaultRetryTimeout) < 0);

if (Message.LoadKvm)
{
LoadKvmModule();
}

response.Result = 0;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
response.Result = wil::ResultFromCaughtException();
}

Transaction.Send<WSLC_MOUNT_RESULT>(response);
}

void HandleMessageImpl(
wsl::shared::SocketChannel& Channel, wsl::shared::Transaction& Transaction, const WSLC_EXEC& Message, const gsl::span<gsl::byte>& Buffer)
{
Expand Down Expand Up @@ -1079,7 +1144,7 @@ void ProcessMessage(wsl::shared::SocketChannel& Channel, wsl::shared::Transactio
{
try
{
HandleMessage<WSLC_GET_DISK, WSLC_MOUNT, WSLC_MOUNT_VIRTIOFS, WSLC_EXEC, WSLC_FORK, WSLC_CONNECT, WSLC_SIGNAL, WSLC_TTY_RELAY, WSLC_PORT_RELAY, WSLC_UNMOUNT, WSLC_DETACH, WSLC_ACCEPT, WSLC_WATCH_PROCESSES, WSLC_UNIX_CONNECT, WSLC_GET_GUEST_CAPABILITIES, WSLC_LISTDIR, WSLC_WRITE_FILE>(
HandleMessage<WSLC_GET_DISK, WSLC_MOUNT, WSLC_MOUNT_VIRTIOFS, WSLC_MOUNT_MODULES, WSLC_EXEC, WSLC_FORK, WSLC_CONNECT, WSLC_SIGNAL, WSLC_TTY_RELAY, WSLC_PORT_RELAY, WSLC_UNMOUNT, WSLC_DETACH, WSLC_ACCEPT, WSLC_WATCH_PROCESSES, WSLC_UNIX_CONNECT, WSLC_GET_GUEST_CAPABILITIES, WSLC_LISTDIR, WSLC_WRITE_FILE>(
Channel, Transaction, Type, Buffer);
}
catch (...)
Expand Down
20 changes: 18 additions & 2 deletions src/shared/inc/lxinitshared.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ typedef enum _LX_MESSAGE_TYPE
LxMessageWSLCListDirResult,
LxMessageWSLCMountVirtioFs,
LxMessageWSLCWriteFile,
LxMessageWSLCMountModules,
} LX_MESSAGE_TYPE,
*PLX_MESSAGE_TYPE;

Expand Down Expand Up @@ -531,6 +532,7 @@ inline auto ToString(LX_MESSAGE_TYPE messageType)
X(LxMessageWSLCListDirResult)
X(LxMessageWSLCMountVirtioFs)
X(LxMessageWSLCWriteFile)
X(LxMessageWSLCMountModules)

default:
return "<unexpected LX_MESSAGE_TYPE>";
Expand Down Expand Up @@ -1652,8 +1654,7 @@ struct WSLC_MOUNT
None,
ReadOnly = 1,
Chroot = 2,
OverlayFs = 4,
KernelModules = 8
OverlayFs = 4
};

char Buffer[];
Expand All @@ -1680,6 +1681,21 @@ struct WSLC_MOUNT_VIRTIOFS
PRETTY_PRINT(FIELD(Header), STRING_FIELD(SourceIndex), STRING_FIELD(DestinationIndex), STRING_FIELD(TypeIndex), STRING_FIELD(OptionsIndex), STRING_FIELD(ChildNameIndex));
};

struct WSLC_MOUNT_MODULES
Comment thread
benhillis marked this conversation as resolved.
{
static inline auto Type = LxMessageWSLCMountModules;
using TResponse = WSLC_MOUNT_RESULT;

DECLARE_MESSAGE_CTOR(WSLC_MOUNT_MODULES);

MESSAGE_HEADER Header{};
unsigned int SourceIndex{};
bool LoadKvm{};
char Buffer[];

PRETTY_PRINT(FIELD(Header), STRING_FIELD(SourceIndex), FIELD(LoadKvm));
};

struct WSLC_EXEC
{
static inline auto Type = LxMessageWSLCExec;
Expand Down
15 changes: 15 additions & 0 deletions src/windows/WslcSDK/winrt/SessionSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ void SessionSettings::EnableGpu(bool value)
WI_UpdateFlag(m_featureFlags, WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU, value);
}

bool SessionSettings::EnableNestedVirtualization()
{
return WI_IsFlagSet(m_featureFlags, WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION);
}

void SessionSettings::EnableNestedVirtualization(bool value)
{
if (m_sessionSettings)
{
throw hresult_illegal_state_change(L"Cannot change nested virtualization setting after session has been initialized");
}

WI_UpdateFlag(m_featureFlags, WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION, value);
}

WslcSessionSettings* SessionSettings::ToStructPointer()
{
if (m_sessionSettings)
Expand Down
2 changes: 2 additions & 0 deletions src/windows/WslcSDK/winrt/SessionSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct SessionSettings : SessionSettingsT<SessionSettings>
void VhdRequirements(winrt::Microsoft::WSL::Containers::VhdOptions const& value);
bool EnableGpu();
void EnableGpu(bool value);
bool EnableNestedVirtualization();
void EnableNestedVirtualization(bool value);

WslcSessionSettings* ToStructPointer();

Expand Down
1 change: 1 addition & 0 deletions src/windows/WslcSDK/winrt/wslcsdk.idl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace Microsoft.WSL.Containers
Windows.Foundation.IReference<Windows.Foundation.TimeSpan> Timeout;
VhdOptions VhdRequirements;
Boolean EnableGpu;
Boolean EnableNestedVirtualization;
};

runtimeclass Session : Windows.Foundation.IClosable
Expand Down
4 changes: 3 additions & 1 deletion src/windows/WslcSDK/wslcsdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ template <>
struct FlagsTraits<WslcSessionFeatureFlags>
{
using WslcType = WSLCFeatureFlags;
constexpr static WslcSessionFeatureFlags Mask = WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU;
constexpr static WslcSessionFeatureFlags Mask =
static_cast<WslcSessionFeatureFlags>(WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU | WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION);
WSLC_FLAG_VALUE_ASSERT(WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU, WslcFeatureFlagsGPU);
WSLC_FLAG_VALUE_ASSERT(WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION, WslcFeatureFlagsNestedVirtualization);
};

template <>
Expand Down
3 changes: 2 additions & 1 deletion src/windows/WslcSDK/wslcsdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ typedef struct WslcVhdRequirements
typedef enum WslcSessionFeatureFlags
{
WSLC_SESSION_FEATURE_FLAG_NONE = 0x00000000,
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU = 0x00000004
WSLC_SESSION_FEATURE_FLAG_ENABLE_GPU = 0x00000004,
WSLC_SESSION_FEATURE_FLAG_NESTED_VIRTUALIZATION = 0x00000040
} WslcSessionFeatureFlags;

DEFINE_ENUM_FLAG_OPERATORS(WslcSessionFeatureFlags);
Expand Down
12 changes: 12 additions & 0 deletions src/windows/common/hcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Module Name:
#include "precomp.h"
#include "hcs.hpp"
#include <ComputeCore.h>
#include "helpers.hpp"

#pragma hdrstop

Expand Down Expand Up @@ -134,6 +135,17 @@ const std::vector<std::string>& wsl::windows::common::hcs::GetProcessorFeatures(
return g_processorFeatures;
}

bool wsl::windows::common::hcs::IsNestedVirtualizationSupported()
{
if (!wsl::windows::common::helpers::IsWindows11OrAbove())
{
return false;
}

const auto& processorFeatures = GetProcessorFeatures();
return std::find(processorFeatures.begin(), processorFeatures.end(), "NestedVirt") != processorFeatures.end();
}

wsl::shared::hns::HNSEndpoint wsl::windows::common::hcs::GetEndpointProperties(HCN_ENDPOINT Endpoint)
{
WSL_LOG_DEBUG("HcsGetEndpointProperties");
Expand Down
2 changes: 2 additions & 0 deletions src/windows/common/hcs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ wsl::shared::hns::HNSEndpoint GetEndpointProperties(HCN_ENDPOINT endpoint);

const std::vector<std::string>& GetProcessorFeatures();

bool IsNestedVirtualizationSupported();

GUID GetRuntimeId(_In_ HCS_SYSTEM ComputeSystem);

std::pair<uint32_t, uint32_t> GetSchemaVersion();
Expand Down
Loading
Loading