1
2
3 package hcsoci
4
5 import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "os"
10 "path/filepath"
11 "strconv"
12
13 specs "github.com/opencontainers/runtime-spec/specs-go"
14 "github.com/pkg/errors"
15
16 "github.com/Microsoft/hcsshim/internal/devices"
17 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
18 "github.com/Microsoft/hcsshim/internal/log"
19 "github.com/Microsoft/hcsshim/internal/oci"
20 "github.com/Microsoft/hcsshim/internal/resources"
21 "github.com/Microsoft/hcsshim/internal/uvm"
22 "github.com/Microsoft/hcsshim/osversion"
23 "github.com/Microsoft/hcsshim/pkg/annotations"
24 )
25
26 const deviceUtilExeName = "device-util.exe"
27
28
29
30 func getSpecKernelDrivers(annots map[string]string) ([]string, error) {
31 drivers := oci.ParseAnnotationCommaSeparated(annotations.VirtualMachineKernelDrivers, annots)
32 for _, driver := range drivers {
33 if _, err := os.Stat(driver); err != nil {
34 return nil, errors.Wrapf(err, "failed to find path to drivers at %s", driver)
35 }
36 }
37 return drivers, nil
38 }
39
40
41
42 func getDeviceExtensionPaths(annots map[string]string) ([]string, error) {
43 extensions := oci.ParseAnnotationCommaSeparated(annotations.DeviceExtensions, annots)
44 for _, ext := range extensions {
45 if _, err := os.Stat(ext); err != nil {
46 return nil, errors.Wrapf(err, "failed to find path to driver extensions at %s", ext)
47 }
48 }
49 return extensions, nil
50 }
51
52
53
54
55 func getGPUVHDPath(annot map[string]string) (string, error) {
56 gpuVHDPath, ok := annot[annotations.GPUVHDPath]
57 if !ok || gpuVHDPath == "" {
58 return "", errors.New("no gpu vhd specified")
59 }
60 if _, err := os.Stat(gpuVHDPath); err != nil {
61 return "", errors.Wrapf(err, "failed to find gpu support vhd %s", gpuVHDPath)
62 }
63 return gpuVHDPath, nil
64 }
65
66
67 func getDeviceUtilHostPath() string {
68 return filepath.Join(filepath.Dir(os.Args[0]), deviceUtilExeName)
69 }
70
71 func isDeviceExtensionsSupported() bool {
72
73 return osversion.Build() >= osversion.LTSC2022
74 }
75
76
77
78 func getDeviceExtensions(annotations map[string]string) (*hcsschema.ContainerDefinitionDevice, error) {
79 extensionPaths, err := getDeviceExtensionPaths(annotations)
80 if err != nil {
81 return nil, err
82 }
83
84 if len(extensionPaths) == 0 {
85 return nil, nil
86 }
87
88 if !isDeviceExtensionsSupported() {
89 return nil, fmt.Errorf("device extensions are not supported on this build (%d)", osversion.Build())
90 }
91
92 results := &hcsschema.ContainerDefinitionDevice{
93 DeviceExtension: []hcsschema.DeviceExtension{},
94 }
95 for _, extensionPath := range extensionPaths {
96 data, err := os.ReadFile(extensionPath)
97 if err != nil {
98 return nil, errors.Wrapf(err, "failed to read extension file at %s", extensionPath)
99 }
100 extension := hcsschema.DeviceExtension{}
101 if err := json.Unmarshal(data, &extension); err != nil {
102 return nil, errors.Wrapf(err, "failed to unmarshal extension file at %s", extensionPath)
103 }
104 results.DeviceExtension = append(results.DeviceExtension, extension)
105 }
106 return results, nil
107 }
108
109
110
111
112
113
114
115 func handleAssignedDevicesWindows(
116 ctx context.Context,
117 vm *uvm.UtilityVM,
118 annotations map[string]string,
119 specDevs []specs.WindowsDevice) (resultDevs []specs.WindowsDevice, closers []resources.ResourceCloser, err error) {
120 defer func() {
121 if err != nil {
122
123 for _, r := range closers {
124 if releaseErr := r.Release(ctx); releaseErr != nil {
125 log.G(ctx).WithError(releaseErr).Error("failed to release container resource")
126 }
127 }
128 closers = nil
129 resultDevs = nil
130 }
131 }()
132
133
134 toolHostPath := getDeviceUtilHostPath()
135 options := vm.DefaultVSMBOptions(true)
136 toolsShare, err := vm.AddVSMB(ctx, toolHostPath, options)
137 if err != nil {
138 return nil, closers, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", toolHostPath, err)
139 }
140 closers = append(closers, toolsShare)
141 deviceUtilPath, err := vm.GetVSMBUvmPath(ctx, toolHostPath, true)
142 if err != nil {
143 return nil, closers, err
144 }
145
146
147 for _, d := range specDevs {
148 pciID, index := getDeviceInfoFromPath(d.ID)
149 vpciCloser, locationPaths, err := devices.AddDevice(ctx, vm, d.IDType, pciID, index, deviceUtilPath)
150 if err != nil {
151 return nil, nil, err
152 }
153 closers = append(closers, vpciCloser)
154 for _, value := range locationPaths {
155 specDev := specs.WindowsDevice{
156 ID: value,
157 IDType: uvm.VPCILocationPathIDType,
158 }
159 log.G(ctx).WithField("parsed devices", specDev).Info("added windows device to spec")
160 resultDevs = append(resultDevs, specDev)
161 }
162 }
163
164 return resultDevs, closers, nil
165 }
166
167
168
169
170
171
172 func handleAssignedDevicesLCOW(
173 ctx context.Context,
174 vm *uvm.UtilityVM,
175 annotations map[string]string,
176 specDevs []specs.WindowsDevice) (resultDevs []specs.WindowsDevice, closers []resources.ResourceCloser, err error) {
177 defer func() {
178 if err != nil {
179
180 for _, r := range closers {
181 if releaseErr := r.Release(ctx); releaseErr != nil {
182 log.G(ctx).WithError(releaseErr).Error("failed to release container resource")
183 }
184 }
185 closers = nil
186 resultDevs = nil
187 }
188 }()
189
190 gpuPresent := false
191
192
193 for _, d := range specDevs {
194 switch d.IDType {
195 case uvm.VPCIDeviceIDType, uvm.VPCIDeviceIDTypeLegacy, uvm.GPUDeviceIDType:
196 gpuPresent = gpuPresent || d.IDType == uvm.GPUDeviceIDType
197 pciID, index := getDeviceInfoFromPath(d.ID)
198 vpci, err := vm.AssignDevice(ctx, pciID, index, "")
199 if err != nil {
200 return resultDevs, closers, errors.Wrapf(err, "failed to assign device %s, function %d to pod %s", pciID, index, vm.ID())
201 }
202 closers = append(closers, vpci)
203
204
205
206 d.ID = vpci.VMBusGUID
207 resultDevs = append(resultDevs, d)
208 default:
209 return resultDevs, closers, errors.Errorf("specified device %s has unsupported type %s", d.ID, d.IDType)
210 }
211 }
212
213 if gpuPresent {
214 gpuSupportVhdPath, err := getGPUVHDPath(annotations)
215 if err != nil {
216 return resultDevs, closers, errors.Wrapf(err, "failed to add gpu vhd to %v", vm.ID())
217 }
218
219 driverCloser, err := devices.InstallDrivers(ctx, vm, gpuSupportVhdPath, true)
220 if err != nil {
221 return resultDevs, closers, err
222 }
223 if driverCloser != nil {
224 closers = append(closers, driverCloser)
225 }
226 }
227
228 return resultDevs, closers, nil
229 }
230
231
232 func addSpecGuestDrivers(ctx context.Context, vm *uvm.UtilityVM, annotations map[string]string) (closers []resources.ResourceCloser, err error) {
233 defer func() {
234 if err != nil {
235
236 for _, r := range closers {
237 if releaseErr := r.Release(ctx); releaseErr != nil {
238 log.G(ctx).WithError(releaseErr).Error("failed to release container resource")
239 }
240 }
241 }
242 }()
243
244
245 drivers, err := getSpecKernelDrivers(annotations)
246 if err != nil {
247 return closers, err
248 }
249 for _, d := range drivers {
250 driverCloser, err := devices.InstallDrivers(ctx, vm, d, false)
251 if err != nil {
252 return closers, err
253 }
254 if driverCloser != nil {
255 closers = append(closers, driverCloser)
256 }
257 }
258 return closers, err
259 }
260
261 func getDeviceInfoFromPath(rawDevicePath string) (string, uint16) {
262 indexString := filepath.Base(rawDevicePath)
263 index, err := strconv.ParseUint(indexString, 10, 16)
264 if err == nil {
265
266 return filepath.Dir(rawDevicePath), uint16(index)
267 }
268
269 return rawDevicePath, 0
270 }
271
View as plain text