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 }