master
cpp 335 lines 13.1 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 hcs.cpp
8
9 Abstract:
10
11 This file contains helper function definitions for interacting with the
12 host compute service.
13
14 --*/
15
16 #include "precomp.h"
17 #include "hcs.hpp"
18 #include <ComputeCore.h>
19
20 #pragma hdrstop
21
22 using wsl::windows::common::Context;
23 using wsl::windows::common::ExecutionContext;
24
25 constexpr auto c_processorCapabilities = "ProcessorCapabilities";
26 constexpr LPCWSTR c_processorCapabilitiesQuery = L"{ \"PropertyQueries\": {\"ProcessorCapabilities\" : {}}}";
27 constexpr LPCWSTR c_scsiResourcePath = L"VirtualMachine/Devices/Scsi/0/Attachments/";
28
29 void wsl::windows::common::hcs::AddPlan9Share(
30 _In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Name, _In_ PCWSTR AccessName, _In_ PCWSTR Path, _In_ UINT32 Port, _In_ Plan9ShareFlags Flags, _In_opt_ HANDLE UserToken)
31 {
32 ModifySettingRequest<Plan9Share> request{};
33 request.RequestType = ModifyRequestType::Add;
34 request.ResourcePath = L"VirtualMachine/Devices/Plan9/Shares";
35 request.Settings.Name = Name;
36 request.Settings.AccessName = AccessName;
37 request.Settings.Path = Path;
38 request.Settings.Port = Port;
39 WI_SetFlagIf(Flags, Plan9ShareFlags::UseShareRootIdentity, ARGUMENT_PRESENT(UserToken));
40 request.Settings.Flags = Flags;
41
42 ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str(), UserToken);
43 }
44
45 void wsl::windows::common::hcs::RemovePlan9Share(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR AccessName, _In_ UINT32 Port)
46 {
47 ModifySettingRequest<Plan9Share> request{};
48 request.RequestType = ModifyRequestType::Remove;
49 request.ResourcePath = L"VirtualMachine/Devices/Plan9/Shares";
50 request.Settings.AccessName = AccessName;
51 request.Settings.Port = Port;
52
53 ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());
54 }
55
56 void wsl::windows::common::hcs::AddVhd(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR VhdPath, _In_ ULONG Lun, _In_ bool ReadOnly)
57 {
58 ModifySettingRequest<Attachment> request{};
59 request.RequestType = ModifyRequestType::Add;
60 request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);
61 request.Settings.Path = VhdPath;
62 request.Settings.ReadOnly = ReadOnly;
63 request.Settings.Type = AttachmentType::VirtualDisk;
64 request.Settings.SupportCompressedVolumes = true;
65 request.Settings.AlwaysAllowSparseFiles = true;
66 request.Settings.SupportEncryptedFiles = true;
67
68 ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());
69 }
70
71 void wsl::windows::common::hcs::AddPassThroughDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Disk, _In_ ULONG Lun)
72 {
73 ModifySettingRequest<Attachment> request{};
74 request.RequestType = ModifyRequestType::Add;
75 request.Settings.Path = Disk;
76 request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);
77 request.Settings.Type = AttachmentType::PassThru;
78
79 ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());
80 }
81
82 wsl::windows::common::hcs::unique_hcs_operation wsl::windows::common::hcs::CreateOperation()
83 {
84 unique_hcs_operation operation(::HcsCreateOperation(nullptr, nullptr));
85 THROW_LAST_ERROR_IF_MSG(!operation, "HcsCreateOperation");
86
87 return operation;
88 }
89
90 wsl::windows::common::hcs::unique_hcs_system wsl::windows::common::hcs::CreateComputeSystem(_In_ PCWSTR Id, _In_ PCWSTR Configuration)
91 {
92 WSL_LOG_DEBUG("HcsCreateComputeSystem", TraceLoggingValue(Id, "id"), TraceLoggingValue(Configuration, "configuration"));
93
94 ExecutionContext context(Context::HCS);
95
96 const unique_hcs_operation operation = CreateOperation();
97 unique_hcs_system system{};
98 THROW_IF_FAILED(::HcsCreateComputeSystem(Id, Configuration, operation.get(), nullptr, &system));
99
100 wil::unique_cotaskmem_string resultDocument;
101 const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);
102 if (FAILED(result))
103 {
104 // N.B. Logging is split into two calls because the configuration and error strings can be quite long.
105 LOG_HR_MSG(result, "HcsCreateComputeSystem(%ls, %ls)", Id, Configuration);
106 THROW_HR_MSG(result, "HcsCreateComputeSystem failed (error string: %ls)", resultDocument.get());
107 }
108
109 return system;
110 }
111
112 const std::vector<std::string>& wsl::windows::common::hcs::GetProcessorFeatures()
113 {
114 static std::vector<std::string> g_processorFeatures;
115 static std::once_flag flag;
116 std::call_once(flag, []() {
117 ExecutionContext context(Context::HCS);
118
119 wil::unique_cotaskmem_string result;
120 THROW_IF_FAILED(::HcsGetServiceProperties(c_processorCapabilitiesQuery, &result));
121
122 const auto properties =
123 wsl::shared::FromJson<ServicePropertiesResponse<PropertyResponse<ProcessorCapabilitiesInfo>>>(result.get());
124
125 const auto& response = properties.PropertyResponses.at(c_processorCapabilities);
126 if (response.Error)
127 {
128 THROW_HR_MSG(static_cast<HRESULT>(response.Error->Error), "%hs", response.Error->ErrorMessage.c_str());
129 }
130
131 g_processorFeatures = response.Response.ProcessorFeatures;
132 });
133
134 return g_processorFeatures;
135 }
136
137 wsl::shared::hns::HNSEndpoint wsl::windows::common::hcs::GetEndpointProperties(HCN_ENDPOINT Endpoint)
138 {
139 WSL_LOG_DEBUG("HcsGetEndpointProperties");
140
141 ExecutionContext context(Context::HNS);
142
143 wil::unique_cotaskmem_string propertiesString;
144 wil::unique_cotaskmem_string error;
145 const auto result = HcnQueryEndpointProperties(Endpoint, nullptr, &propertiesString, &error);
146 THROW_IF_FAILED_MSG(result, "HcnQueryEndpointProperties %ls", error.get());
147
148 return wsl::shared::FromJson<wsl::shared::hns::HNSEndpoint>(propertiesString.get());
149 }
150
151 GUID wsl::windows::common::hcs::GetRuntimeId(_In_ HCS_SYSTEM ComputeSystem)
152 {
153 ExecutionContext context(Context::HCS);
154
155 const unique_hcs_operation operation = CreateOperation();
156 THROW_IF_FAILED(::HcsGetComputeSystemProperties(ComputeSystem, operation.get(), nullptr));
157
158 wil::unique_cotaskmem_string resultDocument;
159 const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);
160 THROW_IF_FAILED_MSG(result, "HcsGetComputeSystemProperties failed (error string: %ls)", resultDocument.get());
161
162 const auto properties = wsl::shared::FromJson<Properties>(resultDocument.get());
163 THROW_HR_IF(HCS_E_SYSTEM_NOT_FOUND, (properties.SystemType != SystemType::VirtualMachine));
164
165 return properties.RuntimeId;
166 }
167
168 std::pair<uint32_t, uint32_t> wsl::windows::common::hcs::GetSchemaVersion()
169 {
170 static std::pair<uint32_t, uint32_t> g_schemaVersion{};
171 static std::once_flag flag;
172 std::call_once(flag, []() {
173 ExecutionContext context(Context::HCS);
174
175 PropertyQuery query;
176 query.PropertyTypes.emplace_back(PropertyType::Basic);
177 wil::unique_cotaskmem_string result;
178 THROW_IF_FAILED(::HcsGetServiceProperties(wsl::shared::ToJsonW(query).c_str(), &result));
179
180 const auto properties = wsl::shared::FromJson<ServiceProperties<BasicInformation>>(result.get());
181 THROW_HR_IF_MSG(E_UNEXPECTED, properties.Properties.empty(), "%ls", result.get());
182
183 uint32_t majorVersion = 0;
184 uint32_t minorVersion = 0;
185 for (const auto& version : properties.Properties[0].SupportedSchemaVersions)
186 {
187 if (version.Major >= majorVersion)
188 {
189 if ((version.Major > majorVersion) || (version.Minor > minorVersion))
190 {
191 majorVersion = version.Major;
192 minorVersion = version.Minor;
193 }
194 }
195 }
196
197 g_schemaVersion = {majorVersion, minorVersion};
198 });
199
200 return g_schemaVersion;
201 }
202
203 void wsl::windows::common::hcs::GrantVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath)
204 {
205 WSL_LOG_DEBUG("HcsGrantVmAccess", TraceLoggingValue(VmId, "vmId"), TraceLoggingValue(FilePath, "filePath"));
206
207 ExecutionContext context(Context::HCS);
208
209 THROW_IF_FAILED_MSG(::HcsGrantVmAccess(VmId, FilePath), "HcsGrantVmAccess(%ls, %ls)", VmId, FilePath);
210 }
211
212 void wsl::windows::common::hcs::ModifyComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ PCWSTR Configuration, _In_opt_ HANDLE Identity)
213 {
214 WSL_LOG_DEBUG("HcsModifyComputeSystem", TraceLoggingValue(Configuration, "configuration"));
215
216 ExecutionContext context(Context::HCS);
217
218 const unique_hcs_operation operation = CreateOperation();
219 THROW_IF_FAILED_MSG(
220 ::HcsModifyComputeSystem(ComputeSystem, operation.get(), Configuration, Identity), "HcsModifyComputeSystem (%ls)", Configuration);
221
222 wil::unique_cotaskmem_string resultDocument;
223 const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);
224 if (FAILED(result))
225 {
226 // N.B. Logging is split into two calls because the configuration and error strings can be quite long.
227 LOG_HR_MSG(result, "HcsModifyComputeSystem(%ls)", Configuration);
228 THROW_HR_MSG(result, "HcsModifyComputeSystem failed (error string: %ls)", resultDocument.get());
229 }
230 }
231
232 wsl::windows::common::hcs::unique_hcs_system wsl::windows::common::hcs::OpenComputeSystem(_In_ PCWSTR Id, _In_ DWORD RequestedAccess)
233 {
234 WSL_LOG_DEBUG("HcsOpenComputeSystem", TraceLoggingValue(Id, "id"), TraceLoggingValue(RequestedAccess, "requestedAccess"));
235
236 ExecutionContext context(Context::HCS);
237
238 unique_hcs_system system;
239 THROW_IF_FAILED_MSG(::HcsOpenComputeSystem(Id, RequestedAccess, &system), "HcsOpenComputeSystem(%ls)", Id);
240
241 return system;
242 }
243
244 void wsl::windows::common::hcs::RegisterCallback(_In_ HCS_SYSTEM ComputeSystem, _In_ HCS_EVENT_CALLBACK Callback, _In_ void* Context)
245 {
246 WSL_LOG_DEBUG("HcsSetComputeSystemCallback");
247
248 ExecutionContext context(Context::HCS);
249
250 THROW_IF_FAILED(::HcsSetComputeSystemCallback(ComputeSystem, HcsEventOptionNone, Context, Callback));
251 }
252
253 void wsl::windows::common::hcs::RemoveScsiDisk(_In_ HCS_SYSTEM ComputeSystem, _In_ ULONG Lun)
254 {
255 ModifySettingRequest<void> request{};
256 request.RequestType = ModifyRequestType::Remove;
257 request.ResourcePath = c_scsiResourcePath + std::to_wstring(Lun);
258 ModifyComputeSystem(ComputeSystem, wsl::shared::ToJsonW(request).c_str());
259 }
260
261 void wsl::windows::common::hcs::RevokeVmAccess(_In_ PCWSTR VmId, _In_ PCWSTR FilePath)
262 {
263 WSL_LOG_DEBUG("HcsRevokeVmAccess", TraceLoggingValue(VmId, "vmId"), TraceLoggingValue(FilePath, "filePath"));
264
265 ExecutionContext context(Context::HCS);
266
267 THROW_IF_FAILED_MSG(::HcsRevokeVmAccess(VmId, FilePath), "HcsRevokeVmAccess(%ls, %ls)", VmId, FilePath);
268 }
269
270 void wsl::windows::common::hcs::StartComputeSystem(_In_ HCS_SYSTEM ComputeSystem, _In_ LPCWSTR Configuration)
271 {
272 WSL_LOG_DEBUG("HcsStartComputeSystem", TraceLoggingValue(Configuration, "configuration"));
273
274 ExecutionContext context(Context::HCS);
275
276 const unique_hcs_operation operation = CreateOperation();
277 THROW_IF_FAILED(::HcsStartComputeSystem(ComputeSystem, operation.get(), nullptr));
278
279 wil::unique_cotaskmem_string resultDocument;
280 const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);
281 if (FAILED(result))
282 {
283 // N.B. Logging is split into two calls because the configuration and error strings can be quite long.
284 LOG_HR_MSG(result, "HcsStartComputeSystem(%ls)", Configuration);
285 THROW_HR_MSG(result, "HcsStartComputeSystem failed (error string: %ls)", resultDocument.get());
286 }
287 }
288
289 void wsl::windows::common::hcs::TerminateComputeSystem(_In_ HCS_SYSTEM ComputeSystem)
290 {
291 WSL_LOG_DEBUG("HcsTerminateComputeSystem");
292
293 ExecutionContext context(Context::HCS);
294
295 const unique_hcs_operation operation = CreateOperation();
296 THROW_IF_FAILED(::HcsTerminateComputeSystem(ComputeSystem, operation.get(), nullptr));
297
298 wil::unique_cotaskmem_string resultDocument;
299 const auto result = ::HcsWaitForOperationResult(operation.get(), INFINITE, &resultDocument);
300 THROW_IF_FAILED_MSG(result, "HcsTerminateComputeSystem failed (error string: %ls)", resultDocument.get());
301 }
302
303 wsl::windows::common::hcs::unique_hcn_service_callback wsl::windows::common::hcs::RegisterServiceCallback(
304 _In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context)
305 {
306 WSL_LOG_DEBUG("HcsRegisterServiceCallback");
307
308 ExecutionContext context(Context::HNS);
309
310 unique_hcn_service_callback callbackHandle;
311 THROW_IF_FAILED(::HcnRegisterServiceCallback(Callback, Context, &callbackHandle));
312
313 return callbackHandle;
314 }
315
316 wsl::windows::common::hcs::unique_hcn_guest_network_service_callback wsl::windows::common::hcs::RegisterGuestNetworkServiceCallback(
317 _In_ const unique_hcn_guest_network_service& GuestNetworkService, _In_ HCS_NOTIFICATION_CALLBACK Callback, _In_ PVOID Context)
318 {
319 WSL_LOG_DEBUG("HcsRegisterGuestNetworkServiceCallback");
320
321 ExecutionContext context(Context::HNS);
322
323 unique_hcn_guest_network_service_callback callbackHandle;
324 THROW_IF_FAILED(::HcnRegisterGuestNetworkServiceCallback(GuestNetworkService.get(), Callback, Context, &callbackHandle));
325
326 return callbackHandle;
327 }
328
329 bool wsl::windows::common::hcs::IsDisableVgpuSettingsSupported()
330 {
331 static constexpr std::pair<uint32_t, uint32_t> c_schemaVersionNickel{2, 7};
332
333 // See if the Windows version has the required platform change.
334 return ((GetSchemaVersion() >= c_schemaVersionNickel) && (wsl::windows::common::helpers::GetWindowsVersion().BuildNumber >= 22545));
335 }