...

Source file src/github.com/Microsoft/hcsshim/internal/devices/drivers.go

Documentation: github.com/Microsoft/hcsshim/internal/devices

     1  //go:build windows
     2  // +build windows
     3  
     4  package devices
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/Microsoft/go-winio/pkg/guid"
    11  	"github.com/Microsoft/hcsshim/internal/cmd"
    12  	"github.com/Microsoft/hcsshim/internal/guestpath"
    13  	"github.com/Microsoft/hcsshim/internal/log"
    14  	"github.com/Microsoft/hcsshim/internal/resources"
    15  	"github.com/Microsoft/hcsshim/internal/uvm"
    16  )
    17  
    18  // InstallDriver mounts a share from the host into the UVM, installs any kernel drivers in the share,
    19  // and configures the environment for library files and/or binaries in the share.
    20  //
    21  // InstallDriver mounts a specified kernel driver, then installs it in the UVM.
    22  //
    23  // `share` is a directory path on the host that contains files for standard driver installation.
    24  // For windows this means files for pnp installation (.inf, .cat, .sys, .cert files).
    25  // For linux this means a vhd file that contains the drivers under /lib/modules/`uname -r` for use
    26  // with depmod and modprobe. For GPU, this vhd may also contain library files (under /usr/lib) and
    27  // binaries (under /usr/bin or /usr/sbin) to be used in conjunction with the modules.
    28  //
    29  // Returns a ResourceCloser for the added mount. On failure, the mounted share will be released,
    30  // the returned ResourceCloser will be nil, and an error will be returned.
    31  func InstallDrivers(ctx context.Context, vm *uvm.UtilityVM, share string, gpuDriver bool) (closer resources.ResourceCloser, err error) {
    32  	defer func() {
    33  		if err != nil && closer != nil {
    34  			// best effort clean up allocated resource on failure
    35  			if releaseErr := closer.Release(ctx); releaseErr != nil {
    36  				log.G(ctx).WithError(releaseErr).Error("failed to release container resource")
    37  			}
    38  			closer = nil
    39  		}
    40  	}()
    41  	if vm.OS() == "windows" {
    42  		options := vm.DefaultVSMBOptions(true)
    43  		closer, err = vm.AddVSMB(ctx, share, options)
    44  		if err != nil {
    45  			return closer, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", share, err)
    46  		}
    47  		uvmPath, err := vm.GetVSMBUvmPath(ctx, share, true)
    48  		if err != nil {
    49  			return closer, err
    50  		}
    51  		// attempt to install even if the driver has already been installed before so we
    52  		// can guarantee the device is ready for use afterwards
    53  		return closer, execPnPInstallDriver(ctx, vm, uvmPath)
    54  	}
    55  
    56  	// first mount driver as scsi in standard mount location
    57  	uvmPathForShare := fmt.Sprintf(guestpath.LCOWGlobalMountPrefixFmt, vm.UVMMountCounter())
    58  	mount, err := vm.AddSCSI(ctx,
    59  		share,
    60  		uvmPathForShare,
    61  		true,
    62  		false,
    63  		[]string{},
    64  		uvm.VMAccessTypeIndividual)
    65  	if err != nil {
    66  		return closer, fmt.Errorf("failed to add SCSI disk to utility VM for path %+v: %s", share, err)
    67  	}
    68  	closer = mount
    69  	uvmPathForShare = mount.UVMPath
    70  
    71  	// construct path that the drivers will be remounted as read/write in the UVM
    72  
    73  	// 914aadc8-f700-4365-8016-ddad0a9d406d. Random GUID chosen for namespace.
    74  	ns := guid.GUID{Data1: 0x914aadc8, Data2: 0xf700, Data3: 0x4365, Data4: [8]byte{0x80, 0x16, 0xdd, 0xad, 0x0a, 0x9d, 0x40, 0x6d}}
    75  	driverGUID, err := guid.NewV5(ns, []byte(share))
    76  	if err != nil {
    77  		return closer, fmt.Errorf("failed to create a guid path for driver %+v: %s", share, err)
    78  	}
    79  	uvmReadWritePath := fmt.Sprintf(guestpath.LCOWGlobalDriverPrefixFmt, driverGUID.String())
    80  	if gpuDriver {
    81  		// if installing gpu drivers in lcow, use the nvidia mount path instead
    82  		uvmReadWritePath = guestpath.LCOWNvidiaMountPath
    83  	}
    84  
    85  	// install drivers using gcs tool `install-drivers`
    86  	return closer, execGCSInstallDriver(ctx, vm, uvmPathForShare, uvmReadWritePath)
    87  }
    88  
    89  func execGCSInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string, driverReadWriteDir string) error {
    90  	p, l, err := cmd.CreateNamedPipeListener()
    91  	if err != nil {
    92  		return err
    93  	}
    94  	defer l.Close()
    95  
    96  	var stderrOutput string
    97  	errChan := make(chan error)
    98  
    99  	go readAllPipeOutput(l, errChan, &stderrOutput)
   100  
   101  	args := []string{
   102  		"/bin/install-drivers",
   103  		driverReadWriteDir,
   104  		driverDir,
   105  	}
   106  	req := &cmd.CmdProcessRequest{
   107  		Args:   args,
   108  		Stderr: p,
   109  	}
   110  
   111  	// A call to `ExecInUvm` may fail in the following ways:
   112  	// - The process runs and exits with a non-zero exit code. In this case we need to wait on the output
   113  	//   from stderr so we can log it for debugging.
   114  	// - There's an error trying to run the process. No need to wait for stderr logs.
   115  	// - There's an error copying IO. No need to wait for stderr logs.
   116  	//
   117  	// Since we cannot distinguish between the cases above, we should always wait to read the stderr output.
   118  	exitCode, execErr := cmd.ExecInUvm(ctx, vm, req)
   119  
   120  	// wait to finish parsing stdout results
   121  	select {
   122  	case err := <-errChan:
   123  		if err != nil && err != noExecOutputErr {
   124  			return fmt.Errorf("failed to get stderr output from command %s: %v", driverDir, err)
   125  		}
   126  	case <-ctx.Done():
   127  		return fmt.Errorf("timed out waiting for the console output from installing driver %s: %v", driverDir, ctx.Err())
   128  	}
   129  
   130  	if execErr != nil {
   131  		return fmt.Errorf("%v: failed to install driver %s in uvm with exit code %d: %v", execErr, driverDir, exitCode, stderrOutput)
   132  	}
   133  	return nil
   134  }
   135  

View as plain text