github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/fsimpl/sys/pci.go (about) 1 // Copyright 2023 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sys 16 17 import ( 18 "fmt" 19 "path" 20 regex "regexp" 21 22 "golang.org/x/sys/unix" 23 "github.com/MerlinKodo/gvisor/pkg/abi/linux" 24 "github.com/MerlinKodo/gvisor/pkg/context" 25 "github.com/MerlinKodo/gvisor/pkg/fsutil" 26 "github.com/MerlinKodo/gvisor/pkg/sentry/fsimpl/kernfs" 27 "github.com/MerlinKodo/gvisor/pkg/sentry/kernel/auth" 28 ) 29 30 const ( 31 pciMainBusDevicePath = "/sys/devices/pci0000:00" 32 // Size of the buffer that host file content will be read into. All relevant 33 // host files are smaller than this. 34 hostFileBufSize = 0x1000 35 ) 36 37 var ( 38 // Matches PCI device addresses in the main domain. 39 pciDeviceRegex = regex.MustCompile(`0000:([a-fA-F0-9]{2}|[a-fA-F0-9]{4}):[a-fA-F0-9]{2}\.[a-fA-F0-9]{1,2}`) 40 // Matches the directories for the main bus (i.e. pci000:00), accel, and 41 // individual devices (e.g. 00:00:04.0) 42 sysDevicesDirRegex = regex.MustCompile(`pci0000:00|accel|(0000:([a-fA-F0-9]{2}|[a-fA-F0-9]{4}):[a-fA-F0-9]{2}\.[a-fA-F0-9]{1,2})`) 43 // Files allowlisted for host passthrough. These files are read-only. 44 sysDevicesFiles = map[string]any{ 45 "vendor": nil, "device": nil, "subsystem_vendor": nil, "subsystem_device": nil, 46 "revision": nil, "class": nil, "numa_node": nil, "iommu_group": nil, 47 "resource": nil, "pci_address": nil, "dev": nil, "driver_version": nil, 48 "reset_count": nil, "write_open_count": nil, "status": nil, 49 "is_device_owned": nil, "device_owner": nil, "framework_version": nil, 50 "user_mem_ranges": nil, "interrupt_counts": nil, "chip_model": nil, 51 "bar_offsets": nil, "bar_sizes": nil, "resource0": nil, "resource1": nil, 52 "resource2": nil, "resource3": nil, "resource4": nil, "resource5": nil, 53 } 54 ) 55 56 // Create /sys/class/accel/accel# symlinks. 57 func (fs *filesystem) newAccelDir(ctx context.Context, creds *auth.Credentials) (map[string]kernfs.Inode, error) { 58 accelDirs := map[string]kernfs.Inode{} 59 pciDents, err := hostDirEntries(pciMainBusDevicePath) 60 if err != nil { 61 return nil, err 62 } 63 for _, pciDent := range pciDents { 64 accelDents, err := hostDirEntries(path.Join(pciMainBusDevicePath, pciDent, "accel")) 65 if err != nil { 66 return nil, err 67 } 68 if len(accelDents) != 1 { 69 return nil, fmt.Errorf("path %q should only have one entry", path.Join(pciMainBusDevicePath, pciDent, "accel")) 70 } 71 accelDirs[accelDents[0]] = kernfs.NewStaticSymlink(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), fmt.Sprintf("../../devices/pci0000:00/%s/accel/%s", pciDent, accelDents[0])) 72 } 73 74 return accelDirs, nil 75 } 76 77 // Create /sys/bus/pci/devices symlinks. 78 func (fs *filesystem) newPCIDevicesDir(ctx context.Context, creds *auth.Credentials) (map[string]kernfs.Inode, error) { 79 pciDevicesDir := map[string]kernfs.Inode{} 80 pciDents, err := hostDirEntries(pciMainBusDevicePath) 81 if err != nil { 82 return nil, err 83 } 84 for _, pciDent := range pciDents { 85 pciDevicesDir[pciDent] = kernfs.NewStaticSymlink(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), fmt.Sprintf("../../../devices/pci0000:00/%s", pciDent)) 86 } 87 88 return pciDevicesDir, nil 89 } 90 91 // Recursively build out sysfs directories according to the allowlisted files, 92 // directories, and symlinks defined in this package. 93 func (fs *filesystem) mirrorPCIBusDeviceDir(ctx context.Context, creds *auth.Credentials, dir string) (map[string]kernfs.Inode, error) { 94 subs := map[string]kernfs.Inode{} 95 dents, err := hostDirEntries(dir) 96 if err != nil { 97 return nil, err 98 } 99 for _, dent := range dents { 100 dentPath := path.Join(dir, dent) 101 dentMode, err := hostFileMode(dentPath) 102 if err != nil { 103 return nil, err 104 } 105 switch dentMode { 106 case unix.S_IFDIR: 107 if match := sysDevicesDirRegex.MatchString(dent); !match { 108 continue 109 } 110 contents, err := fs.mirrorPCIBusDeviceDir(ctx, creds, dentPath) 111 if err != nil { 112 return nil, err 113 } 114 subs[dent] = fs.newDir(ctx, creds, defaultSysMode, contents) 115 case unix.S_IFREG: 116 if _, ok := sysDevicesFiles[dent]; ok { 117 subs[dent] = fs.newHostFile(ctx, creds, defaultSysMode, dentPath) 118 } 119 case unix.S_IFLNK: 120 // Both the device and PCI address entries are links to the original PCI 121 // device directory that's at the same place earlier in the dir tree. 122 if match := pciDeviceRegex.MatchString(dent); !(match || dent == "device") { 123 continue 124 } 125 pciDeviceName := pciDeviceRegex.FindString(dir) 126 if pciDeviceName == "" { 127 return nil, fmt.Errorf("could not populate sysfs pci symlink %s", dir) 128 } 129 linkContent := fmt.Sprintf("../../../%s", pciDeviceName) 130 subs[dent] = kernfs.NewStaticSymlink(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linkContent) 131 } 132 } 133 return subs, nil 134 } 135 136 func hostFileMode(path string) (uint32, error) { 137 fd, err := unix.Openat(-1, path, unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_PATH, 0) 138 if err != nil { 139 return 0, err 140 } 141 stat := unix.Stat_t{} 142 if err := unix.Fstat(fd, &stat); err != nil { 143 return 0, err 144 } 145 return stat.Mode & unix.S_IFMT, nil 146 } 147 148 func hostDirEntries(path string) ([]string, error) { 149 fd, err := unix.Openat(-1, path, unix.O_RDONLY|unix.O_NOFOLLOW, 0) 150 if err != nil { 151 return nil, err 152 } 153 var buf [hostFileBufSize]byte 154 n, err := unix.Getdents(fd, buf[:]) 155 if err != nil { 156 return nil, err 157 } 158 var dents []string 159 fsutil.ParseDirents(buf[:n], func(_ uint64, _ int64, _ uint8, name string, _ uint16) bool { 160 dents = append(dents, name) 161 return true 162 }) 163 return dents, nil 164 }