]>
Commit | Line | Data |
---|---|---|
f0765c71 DC |
1 | package PVE::SysFSTools; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use IO::File; | |
7 | ||
8 | use PVE::Tools qw(file_read_firstline dir_glob_foreach); | |
9 | ||
10 | my $pcisysfs = "/sys/bus/pci"; | |
bf94e590 | 11 | my $pciregex = "([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\.([a-f0-9])"; |
f0765c71 DC |
12 | |
13 | sub lspci { | |
14 | ||
15 | my $devices = {}; | |
16 | ||
bf94e590 DC |
17 | dir_glob_foreach("$pcisysfs/devices", $pciregex, sub { |
18 | my (undef, undef, $bus, $slot, $function) = @_; | |
19 | my $id = "$bus:$slot"; | |
f0765c71 DC |
20 | my $res = { id => $id, function => $function}; |
21 | push @{$devices->{$id}}, $res; | |
22 | }); | |
23 | ||
24 | # Entries should be sorted by functions. | |
25 | foreach my $id (keys %$devices) { | |
26 | my $dev = $devices->{$id}; | |
27 | $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ]; | |
28 | } | |
29 | ||
30 | return $devices; | |
31 | } | |
32 | ||
33 | sub check_iommu_support{ | |
34 | #fixme : need to check IOMMU support | |
35 | #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM | |
36 | ||
37 | my $iommu=1; | |
38 | return $iommu; | |
39 | ||
40 | } | |
41 | ||
42 | sub file_write { | |
43 | my ($filename, $buf) = @_; | |
44 | ||
45 | my $fh = IO::File->new($filename, "w"); | |
46 | return undef if !$fh; | |
47 | ||
48 | my $res = print $fh $buf; | |
49 | ||
50 | $fh->close(); | |
51 | ||
52 | return $res; | |
53 | } | |
54 | ||
55 | sub pci_device_info { | |
56 | my ($name) = @_; | |
57 | ||
58 | my $res; | |
59 | ||
bf94e590 | 60 | return undef if $name !~ m/^${pciregex}$/; |
f0765c71 DC |
61 | my ($domain, $bus, $slot, $func) = ($1, $2, $3, $4); |
62 | ||
63 | my $irq = file_read_firstline("$pcisysfs/devices/$name/irq"); | |
64 | return undef if !defined($irq) || $irq !~ m/^\d+$/; | |
65 | ||
66 | my $vendor = file_read_firstline("$pcisysfs/devices/$name/vendor"); | |
67 | return undef if !defined($vendor) || $vendor !~ s/^0x//; | |
68 | ||
69 | my $product = file_read_firstline("$pcisysfs/devices/$name/device"); | |
70 | return undef if !defined($product) || $product !~ s/^0x//; | |
71 | ||
72 | $res = { | |
73 | name => $name, | |
74 | vendor => $vendor, | |
75 | product => $product, | |
76 | domain => $domain, | |
77 | bus => $bus, | |
78 | slot => $slot, | |
79 | func => $func, | |
80 | irq => $irq, | |
81 | has_fl_reset => -f "$pcisysfs/devices/$name/reset" || 0, | |
82 | }; | |
83 | ||
84 | return $res; | |
85 | } | |
86 | ||
87 | sub pci_dev_reset { | |
88 | my ($dev) = @_; | |
89 | ||
90 | my $name = $dev->{name}; | |
91 | ||
92 | my $fn = "$pcisysfs/devices/$name/reset"; | |
93 | ||
94 | return file_write($fn, "1"); | |
95 | } | |
96 | ||
97 | sub pci_dev_bind_to_vfio { | |
98 | my ($dev) = @_; | |
99 | ||
100 | my $name = $dev->{name}; | |
101 | ||
102 | my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; | |
103 | ||
104 | if (!-d $vfio_basedir) { | |
105 | system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); | |
106 | } | |
107 | die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; | |
108 | ||
109 | my $testdir = "$vfio_basedir/$name"; | |
110 | return 1 if -d $testdir; | |
111 | ||
112 | my $data = "$dev->{vendor} $dev->{product}"; | |
113 | return undef if !file_write("$vfio_basedir/new_id", $data); | |
114 | ||
115 | my $fn = "$pcisysfs/devices/$name/driver/unbind"; | |
116 | if (!file_write($fn, $name)) { | |
117 | return undef if -f $fn; | |
118 | } | |
119 | ||
120 | $fn = "$vfio_basedir/bind"; | |
121 | if (! -d $testdir) { | |
122 | return undef if !file_write($fn, $name); | |
123 | } | |
124 | ||
125 | return -d $testdir; | |
126 | } | |
127 | ||
128 | sub pci_dev_group_bind_to_vfio { | |
129 | my ($pciid) = @_; | |
130 | ||
131 | my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; | |
132 | ||
133 | if (!-d $vfio_basedir) { | |
134 | system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); | |
135 | } | |
136 | die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; | |
137 | ||
138 | # get IOMMU group devices | |
139 | opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n"; | |
140 | my @devs = grep /^0000:/, readdir($D); | |
141 | closedir($D); | |
142 | ||
143 | foreach my $pciid (@devs) { | |
144 | $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n"; | |
145 | ||
146 | # pci bridges, switches or root ports are not supported | |
147 | # they have a pci_bus subdirectory so skip them | |
148 | next if (-e "$pcisysfs/devices/$pciid/pci_bus"); | |
149 | ||
150 | my $info = pci_device_info($1); | |
151 | pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n"; | |
152 | } | |
153 | ||
154 | return 1; | |
155 | } | |
156 | ||
157 | 1; |