github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/finddrive/finddrive.go (about)

     1  // Copyright 2022 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package finddrive provides functionality to find an NVMe block device associated with
     6  // a particular physical slot on the machine, based on information in the SMBIOS table.
     7  package finddrive
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/mvdan/u-root-coreutils/pkg/smbios"
    16  )
    17  
    18  const (
    19  	// M2MKeySlotType is the SMBIOS slot type code for M.2 M-key
    20  	M2MKeySlotType = 0x17
    21  )
    22  
    23  // Devices connected to a slot with domain number AAAA, bus number BB, device number CC, and
    24  // function number D are represented as subdirectories of /sys/devices/pciAAAA:BB/AAAA:BB:CC.D/
    25  // These paths are accessible via symlinks in /sys/block for NVMe devices, which can be
    26  // inspected to discover the physical slot associated with that block device.
    27  // For instance, the path /sys/block/nvme0n1 will be a symlink to
    28  // /sys/devices/pciAAAA:BB/AAAA:BB:CC.D/<drive BDF>/nvme/nvme0/nvme0n1
    29  // (The BDF of the device itself is unimportant)
    30  func findBlockDevFromSmbios(sysPath string, s smbios.SystemSlots) ([]string, error) {
    31  	// The SMBIOS table uses the upper 5 bits of this byte to store the device number,
    32  	// and the lower 3 bits to store the function number
    33  	dev := (s.DeviceFunctionNumber & 0b11111000) >> 3
    34  	fn := s.DeviceFunctionNumber & 0b111
    35  	domainBusStr := fmt.Sprintf("%04x:%02x", s.SegmentGroupNumber, s.BusNumber)
    36  	slotBDFPrefix := filepath.Join(sysPath, fmt.Sprintf("devices/pci%s/%s:%02x.%x/", domainBusStr, domainBusStr, dev, fn))
    37  
    38  	blockPath := filepath.Join(sysPath, "block/")
    39  	dirEntries, err := os.ReadDir(blockPath)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	var devPaths []string
    44  	for _, dirEntry := range dirEntries {
    45  		path := filepath.Join(blockPath, dirEntry.Name())
    46  		realPath, err := filepath.EvalSymlinks(path)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		if strings.HasPrefix(realPath, slotBDFPrefix) {
    51  			devPaths = append(devPaths, filepath.Join("/dev", dirEntry.Name()))
    52  		}
    53  	}
    54  	return devPaths, nil
    55  }
    56  
    57  func findSlotType(sysPath string, slots []*smbios.SystemSlots, slotType uint8) ([]string, error) {
    58  	var paths []string
    59  	for _, s := range slots {
    60  		if s.SlotType == slotType {
    61  			newPaths, err := findBlockDevFromSmbios(sysPath, *s)
    62  			if err == nil {
    63  				paths = append(paths, newPaths...)
    64  			}
    65  		}
    66  	}
    67  
    68  	return paths, nil
    69  }
    70  
    71  // FindSlotType searches the SMBIOS table for drives inserted in a slot with the specified type
    72  func FindSlotType(slotType uint8) ([]string, error) {
    73  	smbiosTables, err := smbios.FromSysfs()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	slots, err := smbiosTables.GetSystemSlots()
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	return findSlotType("/sys", slots, slotType)
    83  }