1
2
3 package uvm
4
5 import (
6 "context"
7 "fmt"
8 "os"
9 "path/filepath"
10
11 "github.com/Microsoft/go-winio"
12 "github.com/Microsoft/go-winio/pkg/guid"
13 "github.com/pkg/errors"
14 "go.opencensus.io/trace"
15
16 "github.com/Microsoft/hcsshim/internal/gcs"
17 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
18 "github.com/Microsoft/hcsshim/internal/log"
19 "github.com/Microsoft/hcsshim/internal/logfields"
20 "github.com/Microsoft/hcsshim/internal/oc"
21 "github.com/Microsoft/hcsshim/internal/processorinfo"
22 "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
23 "github.com/Microsoft/hcsshim/internal/schemaversion"
24 "github.com/Microsoft/hcsshim/internal/security"
25 "github.com/Microsoft/hcsshim/internal/uvmfolder"
26 "github.com/Microsoft/hcsshim/internal/wclayer"
27 "github.com/Microsoft/hcsshim/internal/wcow"
28 "github.com/Microsoft/hcsshim/osversion"
29 )
30
31
32 type OptionsWCOW struct {
33 *Options
34
35 LayerFolders []string
36
37
38 NoDirectMap bool
39
40
41 NoInheritHostTimezone bool
42 }
43
44
45
46
47
48
49
50
51 func NewDefaultOptionsWCOW(id, owner string) *OptionsWCOW {
52 return &OptionsWCOW{
53 Options: newDefaultOptions(id, owner),
54 }
55 }
56
57 func (uvm *UtilityVM) startExternalGcsListener(ctx context.Context) error {
58 log.G(ctx).WithField("vmID", uvm.runtimeID).Debug("Using external GCS bridge")
59
60 l, err := winio.ListenHvsock(&winio.HvsockAddr{
61 VMID: uvm.runtimeID,
62 ServiceID: gcs.WindowsGcsHvsockServiceID,
63 })
64 if err != nil {
65 return err
66 }
67 uvm.gcListener = l
68 return nil
69 }
70
71 func prepareConfigDoc(ctx context.Context, uvm *UtilityVM, opts *OptionsWCOW, uvmFolder string) (*hcsschema.ComputeSystem, error) {
72 processorTopology, err := processorinfo.HostProcessorInfo(ctx)
73 if err != nil {
74 return nil, fmt.Errorf("failed to get host processor information: %s", err)
75 }
76
77
78
79 uvm.processorCount = uvm.normalizeProcessorCount(ctx, opts.ProcessorCount, processorTopology)
80
81
82 memorySizeInMB := uvm.normalizeMemorySize(ctx, opts.MemorySizeInMB)
83
84
85 vsmbOpts := uvm.DefaultVSMBOptions(true)
86 vsmbOpts.TakeBackupPrivilege = true
87 virtualSMB := &hcsschema.VirtualSmb{
88 DirectFileMappingInMB: 1024,
89 Shares: []hcsschema.VirtualSmbShare{
90 {
91 Name: "os",
92 Path: filepath.Join(uvmFolder, `UtilityVM\Files`),
93 Options: vsmbOpts,
94 },
95 },
96 }
97
98 var registryChanges hcsschema.RegistryChanges
99
100
101
102
103
104
105
106 if opts.ProcessDumpLocation != "" {
107 uvm.processDumpLocation = opts.ProcessDumpLocation
108 registryChanges.AddValues = append(registryChanges.AddValues,
109 hcsschema.RegistryValue{
110 Key: &hcsschema.RegistryKey{
111 Hive: "System",
112 Name: "ControlSet001\\Services\\WerSvc",
113 },
114 Name: "Start",
115 DWordValue: 2,
116 Type_: "DWord",
117 },
118 hcsschema.RegistryValue{
119 Key: &hcsschema.RegistryKey{
120 Hive: "Software",
121 Name: "Microsoft\\Windows\\Windows Error Reporting",
122 },
123 Name: "Disabled",
124 DWordValue: 1,
125 Type_: "DWord",
126 },
127 )
128 }
129
130
131
132
133
134 if !opts.DisableCompartmentNamespace {
135 registryChanges.AddValues = append(registryChanges.AddValues,
136 hcsschema.RegistryValue{
137 Key: &hcsschema.RegistryKey{
138 Hive: "System",
139 Name: "CurrentControlSet\\Services\\gns",
140 },
141 Name: "EnableCompartmentNamespace",
142 DWordValue: 1,
143 Type_: "DWord",
144 },
145 )
146 }
147
148 processor := &hcsschema.Processor2{
149 Count: uvm.processorCount,
150 Limit: opts.ProcessorLimit,
151 Weight: opts.ProcessorWeight,
152 }
153
154 if opts.CPUGroupID != "" {
155 if osversion.Build() < osversion.V21H1 {
156 return nil, errCPUGroupCreateNotSupported
157 }
158 processor.CpuGroup = &hcsschema.CpuGroup{Id: opts.CPUGroupID}
159 }
160
161 doc := &hcsschema.ComputeSystem{
162 Owner: uvm.owner,
163 SchemaVersion: schemaversion.SchemaV21(),
164 ShouldTerminateOnLastHandleClosed: true,
165 VirtualMachine: &hcsschema.VirtualMachine{
166 StopOnReset: true,
167 Chipset: &hcsschema.Chipset{
168 Uefi: &hcsschema.Uefi{
169 BootThis: &hcsschema.UefiBootEntry{
170 DevicePath: `\EFI\Microsoft\Boot\bootmgfw.efi`,
171 DeviceType: "VmbFs",
172 },
173 },
174 },
175 RegistryChanges: ®istryChanges,
176 ComputeTopology: &hcsschema.Topology{
177 Memory: &hcsschema.Memory2{
178 SizeInMB: memorySizeInMB,
179 AllowOvercommit: opts.AllowOvercommit,
180
181 EnableHotHint: opts.AllowOvercommit,
182 EnableDeferredCommit: opts.EnableDeferredCommit,
183 LowMMIOGapInMB: opts.LowMMIOGapInMB,
184 HighMMIOBaseInMB: opts.HighMMIOBaseInMB,
185 HighMMIOGapInMB: opts.HighMMIOGapInMB,
186 },
187 Processor: processor,
188 },
189 Devices: &hcsschema.Devices{
190 HvSocket: &hcsschema.HvSocket2{
191 HvSocketConfig: &hcsschema.HvSocketSystemConfig{
192
193
194 DefaultBindSecurityDescriptor: "D:P(A;;FA;;;SY)(A;;FA;;;BA)",
195 },
196 },
197 VirtualSmb: virtualSMB,
198 },
199 },
200 }
201
202
203 if opts.StorageQoSBandwidthMaximum > 0 || opts.StorageQoSIopsMaximum > 0 {
204 doc.VirtualMachine.StorageQoS = &hcsschema.StorageQoS{
205 IopsMaximum: opts.StorageQoSIopsMaximum,
206 BandwidthMaximum: opts.StorageQoSBandwidthMaximum,
207 }
208 }
209
210
211 if opts.DumpDirectoryPath != "" {
212 if info, err := os.Stat(opts.DumpDirectoryPath); err != nil {
213 return nil, fmt.Errorf("failed to stat dump directory %s: %w", opts.DumpDirectoryPath, err)
214 } else if !info.IsDir() {
215 return nil, fmt.Errorf("dump directory path %s isn't a directory", opts.DumpDirectoryPath)
216 }
217 if err := security.GrantVmGroupAccessWithMask(opts.DumpDirectoryPath, security.AccessMaskAll); err != nil {
218 return nil, fmt.Errorf("failed to add SDL to dump directory: %w", err)
219 }
220 debugOpts := &hcsschema.DebugOptions{
221 BugcheckSavedStateFileName: filepath.Join(opts.DumpDirectoryPath, fmt.Sprintf("%s-savedstate.vmrs", uvm.id)),
222 BugcheckNoCrashdumpSavedStateFileName: filepath.Join(opts.DumpDirectoryPath, fmt.Sprintf("%s-savedstate_nocrashdump.vmrs", uvm.id)),
223 }
224 doc.VirtualMachine.DebugOptions = debugOpts
225 }
226
227 return doc, nil
228 }
229
230
231
232
233
234 func CreateWCOW(ctx context.Context, opts *OptionsWCOW) (_ *UtilityVM, err error) {
235 ctx, span := oc.StartSpan(ctx, "uvm::CreateWCOW")
236 defer span.End()
237 defer func() { oc.SetSpanStatus(span, err) }()
238
239 if opts.ID == "" {
240 g, err := guid.NewV4()
241 if err != nil {
242 return nil, err
243 }
244 opts.ID = g.String()
245 }
246
247 span.AddAttributes(trace.StringAttribute(logfields.UVMID, opts.ID))
248 log.G(ctx).WithField("options", fmt.Sprintf("%+v", opts)).Debug("uvm::CreateWCOW options")
249
250 uvm := &UtilityVM{
251 id: opts.ID,
252 owner: opts.Owner,
253 operatingSystem: "windows",
254 scsiControllerCount: opts.SCSIControllerCount,
255 vsmbDirShares: make(map[string]*VSMBShare),
256 vsmbFileShares: make(map[string]*VSMBShare),
257 vpciDevices: make(map[VPCIDeviceKey]*VPCIDevice),
258 noInheritHostTimezone: opts.NoInheritHostTimezone,
259 physicallyBacked: !opts.AllowOvercommit,
260 devicesPhysicallyBacked: opts.FullyPhysicallyBacked,
261 vsmbNoDirectMap: opts.NoDirectMap,
262 noWritableFileShares: opts.NoWritableFileShares,
263 createOpts: *opts,
264 }
265
266 defer func() {
267 if err != nil {
268 uvm.Close()
269 }
270 }()
271
272 if err := verifyOptions(ctx, opts); err != nil {
273 return nil, errors.Wrap(err, errBadUVMOpts.Error())
274 }
275
276 uvmFolder, err := uvmfolder.LocateUVMFolder(ctx, opts.LayerFolders)
277 if err != nil {
278 return nil, fmt.Errorf("failed to locate utility VM folder from layer folders: %s", err)
279 }
280
281
282
283
284
285
286
287 scratchFolder := opts.LayerFolders[len(opts.LayerFolders)-1]
288
289
290 if _, err := os.Stat(scratchFolder); os.IsNotExist(err) {
291 if err := os.MkdirAll(scratchFolder, 0777); err != nil {
292 return nil, fmt.Errorf("failed to create utility VM scratch folder: %s", err)
293 }
294 }
295
296 doc, err := prepareConfigDoc(ctx, uvm, opts, uvmFolder)
297 if err != nil {
298 return nil, fmt.Errorf("error in preparing config doc: %s", err)
299 }
300
301
302 scratchPath := filepath.Join(scratchFolder, "sandbox.vhdx")
303 if _, err := os.Stat(scratchPath); os.IsNotExist(err) {
304 if err := wcow.CreateUVMScratch(ctx, uvmFolder, scratchFolder, uvm.id); err != nil {
305 return nil, fmt.Errorf("failed to create scratch: %s", err)
306 }
307 } else {
308
309 if err := wclayer.GrantVmAccess(ctx, uvm.id, scratchPath); err != nil {
310 return nil, errors.Wrap(err, "failed to grant vm access to scratch")
311 }
312 }
313
314 doc.VirtualMachine.Devices.Scsi = map[string]hcsschema.Scsi{}
315 for i := 0; i < int(uvm.scsiControllerCount); i++ {
316 doc.VirtualMachine.Devices.Scsi[guestrequest.ScsiControllerGuids[i]] = hcsschema.Scsi{
317 Attachments: make(map[string]hcsschema.Attachment),
318 }
319 }
320
321 doc.VirtualMachine.Devices.Scsi[guestrequest.ScsiControllerGuids[0]].Attachments["0"] = hcsschema.Attachment{
322
323 Path: scratchPath,
324 Type_: "VirtualDisk",
325 }
326
327 uvm.scsiLocations[0][0] = newSCSIMount(uvm,
328 doc.VirtualMachine.Devices.Scsi[guestrequest.ScsiControllerGuids[0]].Attachments["0"].Path,
329 "",
330 doc.VirtualMachine.Devices.Scsi[guestrequest.ScsiControllerGuids[0]].Attachments["0"].Type_,
331 "",
332 1,
333 0,
334 0,
335 false,
336 false)
337
338 err = uvm.create(ctx, doc)
339 if err != nil {
340 return nil, fmt.Errorf("error while creating the compute system: %s", err)
341 }
342
343 if err = uvm.startExternalGcsListener(ctx); err != nil {
344 return nil, err
345 }
346
347 uvm.ncProxyClientAddress = opts.NetworkConfigProxy
348
349 return uvm, nil
350 }
351
View as plain text