Skip to content
Open
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ endif()
if (DEFINED WSL_DEV_BINARY_PATH) # Development shortcut to make the package smaller
add_compile_definitions(WSL_SYSTEM_DISTRO_PATH="${WSL_DEV_BINARY_PATH}/system.vhd"
WSL_KERNEL_PATH="${WSL_DEV_BINARY_PATH}/kernel"
WSL_KERNEL_MODULES_PATH="${WSL_DEV_BINARY_PATH}/modules.vhd"
WSL_KERNEL_MODULES_PATH="${WSL_DEV_BINARY_PATH}/artifacts.vhd"
WSL_DEV_INSTALL_PATH="${WSL_DEV_BINARY_PATH}"
WSL_GPU_LIB_PATH="${WSL_DEV_BINARY_PATH}/lib")
endif()
Expand Down
4 changes: 2 additions & 2 deletions UserConfig.cmake.sample
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ if(WSL_DEV_BINARY_PATH)
file(MAKE_DIRECTORY ${WSL_DEV_BINARY_PATH})
file(CREATE_LINK "${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/kernel" "${WSL_DEV_BINARY_PATH}/kernel" SYMBOLIC)
file(COPY_FILE "${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/system.vhd" "${WSL_DEV_BINARY_PATH}/system.vhd" ONLY_IF_DIFFERENT)
file(COPY_FILE "${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/modules.vhd" "${WSL_DEV_BINARY_PATH}/modules.vhd" ONLY_IF_DIFFERENT)
file(COPY_FILE "${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/artifacts.vhd" "${WSL_DEV_BINARY_PATH}/artifacts.vhd" ONLY_IF_DIFFERENT)

# read-only VHDs need to be world readable to mount successfully.
execute_process(
COMMAND icacls.exe "${WSL_DEV_BINARY_PATH}/system.vhd" "/grant:r" "Everyone:(R)" /Q
COMMAND icacls.exe "${WSL_DEV_BINARY_PATH}/modules.vhd" "/grant:r" "Everyone:(R)" /Q
COMMAND icacls.exe "${WSL_DEV_BINARY_PATH}/artifacts.vhd" "/grant:r" "Everyone:(R)" /Q
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
COMMAND_ERROR_IS_FATAL ANY)

Expand Down
1 change: 1 addition & 0 deletions doc/docs/technical-documentation/boot-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ When started, the virtual machine will boot into the provided kernel, and then e
- An entropy buffer, to seed the virtual machine's entropy
- Information about the GPU drivers shares to mount, if any
- Whether [wslg](https://github.com/microsoft/wslg) is enabled
- Whether to mount the bundled Linux kernel headers and perf tooling (shipped in the kernel artifacts VHD; headers are mounted at `/usr/src/linux-headers-$(uname -r)` with `/lib/modules/$(uname -r)/build` symlinked to them, and perf is mounted at `/usr/lib/linux-tools/$(uname -r)`, added to the default `$PATH` and, when the distribution ships its own `/usr/bin/perf`, bind mounted over it)

After applying all the configuration requested by [wslservice.exe](wslservice.exe.md), the virtual machine is ready to start Linux distributions.

Expand Down
2 changes: 1 addition & 1 deletion msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@

<?if "${WSL_DEV_BINARY_PATH}" = "" ?>
<File Id="kernel" Source="${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/kernel"/>
<File Id="modules.vhd" Source="${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/modules.vhd"/>
<File Id="artifacts.vhd" Source="${KERNEL_SOURCE_DIR}/bin/${TARGET_PLATFORM}/artifacts.vhd"/>
<?endif?>
</Component>
</DirectoryRef>
Expand Down
2 changes: 1 addition & 1 deletion packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<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.60-0" />
<package id="Microsoft.WSL.Kernel" version="6.18.35.2-1" targetFramework="native" />
<package id="Microsoft.WSL.Kernel" version="6.18.40.1-1" targetFramework="native" />
<package id="Microsoft.WSL.LinuxSdk" version="1.23.0" targetFramework="native" />
<package id="Microsoft.WSL.TestData" version="0.5.0" />
<package id="Microsoft.WSL.TestDistro" version="2.7.1-1" />
Expand Down
63 changes: 44 additions & 19 deletions src/linux/init/WSLCInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ struct WSLCState

static WSLCState g_state;

constexpr auto c_kernelModulesVhdMountPoint = "/kernel_modules_vhd";

void WriteWslcCdiSpec()
try
{
Expand Down Expand Up @@ -696,24 +698,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 +827,46 @@ 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);

const char* source = wsl::shared::string::FromSpan(Buffer, Message.SourceIndex);
THROW_LAST_ERROR_IF(UtilMount(source, c_kernelModulesVhdMountPoint, "ext4", MS_RDONLY, nullptr, c_defaultRetryTimeout) < 0);

auto unmountVhd = wil::scope_exit([&]() {
if (umount(c_kernelModulesVhdMountPoint) < 0)
{
LOG_ERROR("umount({}) failed {}", c_kernelModulesVhdMountPoint, errno);
}
});

g_state.ModulesMountPoint = std::format("/lib/modules/{}", unameBuffer.release);
const std::string modulesSource = std::format("{}/{}/modules", c_kernelModulesVhdMountPoint, unameBuffer.release);
THROW_LAST_ERROR_IF(
UtilMount(modulesSource.c_str(), g_state.ModulesMountPoint->c_str(), nullptr, (MS_BIND | MS_REC), nullptr, c_defaultRetryTimeout) < 0);

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 +1104,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
1 change: 1 addition & 0 deletions src/linux/init/WslDistributionConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ struct WslDistributionConfig

bool GuiAppsEnabled = false;
std::optional<int> FeatureFlags;
std::optional<std::string> KernelPerfPath;
std::optional<LX_MINI_INIT_NETWORKING_MODE> NetworkingMode;
std::optional<std::string> VmId;

Expand Down
120 changes: 110 additions & 10 deletions src/linux/init/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Module Name:
#define LOCALE_FILE_PATH ETC_DEFAULT_FOLDER "locale"
#define LOCALE_CONF_FILE_PATH ETC_FOLDER "locale.conf"
#define PATH_ENV "PATH"
#define PERF_BINARY_PATH "/usr/bin/perf"
#define PERF_EXEC_PATH_ENV "PERF_EXEC_PATH"
#define RESOLV_CONF_DIRECTORY_MODE 0755
#define RESOLV_CONF_FILE_MODE 0644
#define RESOLV_CONF_FILE_NAME "resolv.conf"
Expand Down Expand Up @@ -170,6 +172,32 @@ class RemoveMountAndEnvironmentOnScopeExit
const char* m_mountPath = nullptr;
};

//
// Moves a temporary mount created by mini_init into the distro namespace. The temporary mount point is
// passed via MountEnvironmentName and its final target via PathEnvironmentName. The callback is invoked
// with the target path once the mount has been moved.
//
template <typename TCallback>
static void MoveTemporaryMount(const char* MountEnvironmentName, const char* PathEnvironmentName, const TCallback& Callback)
try
{
auto tempMount = RemoveMountAndEnvironmentOnScopeExit(MountEnvironmentName);
const char* target = tempMount ? getenv(PathEnvironmentName) : nullptr;
if (target == nullptr)
{
return;
}

const std::string targetPath{target};
unsetenv(PathEnvironmentName);
Comment thread
benhillis marked this conversation as resolved.

if (tempMount.MoveMount(targetPath.c_str()))
{
Callback(targetPath);
}
}
CATCH_LOG()

constexpr auto HostsFileFormatString = LX_INIT_AUTO_GENERATED_FILE_HEADER
"# [network]\n"
"# generateHosts = false\n"
Expand Down Expand Up @@ -1110,20 +1138,73 @@ Return Value:
}
CATCH_LOG()

try
{
auto tempMount = RemoveMountAndEnvironmentOnScopeExit(LX_WSL2_KERNEL_MODULES_MOUNT_ENV);
if (tempMount)
std::string kernelModulesPath;
MoveTemporaryMount(LX_WSL2_KERNEL_MODULES_MOUNT_ENV, LX_WSL2_KERNEL_MODULES_PATH_ENV, [&](const std::string& target) {
kernelModulesPath = target;
});

MoveTemporaryMount(LX_WSL2_KERNEL_HEADERS_MOUNT_ENV, LX_WSL2_KERNEL_HEADERS_PATH_ENV, [&](const std::string& target) {
if (kernelModulesPath.empty())
{
return;
}

//
// Point /lib/modules/<release>/build at the kernel headers, replacing any entry that the
// distro may have created so that it can't shadow the headers matching the running kernel.
//
// N.B. A directory can't be replaced with a symlink (and removing it would mean a recursive
// delete), so bind mount the headers over it instead.
//

const std::string linkPath = kernelModulesPath + "/build";
struct stat existing{};
if ((lstat(linkPath.c_str(), &existing) == 0) && S_ISDIR(existing.st_mode))
{
auto target = getenv(LX_WSL2_KERNEL_MODULES_PATH_ENV);
if (target)
if (UtilMount(target.c_str(), linkPath.c_str(), nullptr, (MS_BIND | MS_REC), nullptr) < 0)
{
unsetenv(LX_WSL2_KERNEL_MODULES_PATH_ENV);
tempMount.MoveMount(target);
LOG_ERROR("UtilMount({}, {}) failed {}", target, linkPath, errno);
}

return;
}
}
CATCH_LOG()

if ((unlink(linkPath.c_str()) < 0) && (errno != ENOENT))
Comment thread
benhillis marked this conversation as resolved.
{
LOG_ERROR("unlink({}) failed {}", linkPath, errno);
}

if (symlink(target.c_str(), linkPath.c_str()) < 0)
{
LOG_ERROR("symlink({}, {}) failed {}", target, linkPath, errno);
}
});

MoveTemporaryMount(LX_WSL2_KERNEL_PERF_MOUNT_ENV, LX_WSL2_KERNEL_PERF_PATH_ENV, [&](const std::string& target) {
//
// Expose the kernel-matched perf via the environment block (see ConfigCreateEnvironmentBlock).
//

Config.KernelPerfPath = target;

//
// If the distro ships its own perf, shadow it with a bind mount so that the binary matching the
// running kernel is used.
//
// N.B. The distro's file system is only modified if perf is already present as a regular file.
// Distros without perf pick it up via $PATH instead.
//

struct stat existing{};
if ((lstat(PERF_BINARY_PATH, &existing) == 0) && S_ISREG(existing.st_mode))
{
const std::string perfBinary = target + "/bin/perf";
if (UtilMountFile(perfBinary.c_str(), PERF_BINARY_PATH) < 0)
{
LOG_ERROR("UtilMountFile({}, {}) failed {}", perfBinary, PERF_BINARY_PATH, errno);
}
}
});

//
// Change the permission of some devtmpfs devices to be more permissive.
Expand Down Expand Up @@ -1664,6 +1745,25 @@ Return Value:
{
ConfigAppendToPath(Environment, LXSS_LIB_PATH);
}

//
// Add the kernel-matched perf tools to the $PATH variable and point perf at its helper
// scripts since it is built with a prefix that does not match where it is mounted.
//

if (Config.KernelPerfPath.has_value())
Comment thread
benhillis marked this conversation as resolved.
{
ConfigAppendToPath(Environment, std::format("{}/bin", *Config.KernelPerfPath));

//
// N.B. This is only set if the user has not provided a value via WSLENV.
//

if (Environment.GetVariable(PERF_EXEC_PATH_ENV).empty())
{
Environment.AddVariable(PERF_EXEC_PATH_ENV, std::format("{}/libexec/perf-core", *Config.KernelPerfPath));
}
}
}

//
Expand Down
Loading
Loading