1
|
/*
|
2
|
* xhci-bw-override.c
|
3
|
*
|
4
|
* Small utility that scans for xHCI controller, check if they're compatible
|
5
|
* and overwrite the FS isochronous bandwidth limit register
|
6
|
*
|
7
|
* Copyright (C) 2022 Sylvain Munaut <tnt@246tNt.com>
|
8
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
9
|
*/
|
10
|
|
11
|
#include <dirent.h>
|
12
|
#include <inttypes.h>
|
13
|
#include <fcntl.h>
|
14
|
#include <stdio.h>
|
15
|
#include <stdint.h>
|
16
|
#include <string.h>
|
17
|
#include <unistd.h>
|
18
|
|
19
|
#include <sys/mman.h>
|
20
|
|
21
|
|
22
|
struct pci_dev {
|
23
|
uint16_t vendor;
|
24
|
uint16_t device;
|
25
|
uint32_t class;
|
26
|
char path[PATH_MAX];
|
27
|
};
|
28
|
|
29
|
typedef void (*pci_dev_cb_t)(struct pci_dev *dev);
|
30
|
|
31
|
|
32
|
static int
|
33
|
read_sysfs_hex(const char *filename, uint32_t *val)
|
34
|
{
|
35
|
FILE *f = fopen(filename, "r");
|
36
|
if (!f)
|
37
|
return -1;
|
38
|
if (fscanf(f, "0x%x\n", val) != 1)
|
39
|
return -1;
|
40
|
fclose(f);
|
41
|
return 0;
|
42
|
}
|
43
|
|
44
|
static int
|
45
|
fill_pci_dev(struct pci_dev *dev, const char *path)
|
46
|
{
|
47
|
char fname[512];
|
48
|
uint32_t v;
|
49
|
|
50
|
strncpy(dev->path, path, sizeof(fname));
|
51
|
|
52
|
snprintf(fname, sizeof(fname), "%s/vendor", path);
|
53
|
if (read_sysfs_hex(fname, &v))
|
54
|
return -1;
|
55
|
dev->vendor = v;
|
56
|
|
57
|
snprintf(fname, sizeof(fname), "%s/device", path);
|
58
|
if (read_sysfs_hex(fname, &v))
|
59
|
return -1;
|
60
|
dev->device = v;
|
61
|
|
62
|
snprintf(fname, sizeof(fname), "%s/class", path);
|
63
|
if (read_sysfs_hex(fname, &v))
|
64
|
return -1;
|
65
|
dev->class = v;
|
66
|
|
67
|
return 0;
|
68
|
}
|
69
|
|
70
|
static void
|
71
|
scan_pci_devices(pci_dev_cb_t cb)
|
72
|
{
|
73
|
DIR *dd_root, *dd_bus;
|
74
|
struct dirent *de_root, *de_bus;
|
75
|
char fname[PATH_MAX];
|
76
|
|
77
|
dd_root = opendir("/sys/devices/");
|
78
|
if (!dd_root) {
|
79
|
fprintf(stderr, "[!] Failed to open '/sys/devices/'\n");
|
80
|
perror("opendir");
|
81
|
return;
|
82
|
}
|
83
|
|
84
|
while (1) {
|
85
|
de_root = readdir(dd_root);
|
86
|
if (!de_root)
|
87
|
break;
|
88
|
|
89
|
if (strncmp(de_root->d_name, "pci", 3))
|
90
|
continue;
|
91
|
|
92
|
snprintf(fname, sizeof(fname), "/sys/devices/%s", de_root->d_name);
|
93
|
|
94
|
dd_bus = opendir(fname);
|
95
|
if (!dd_bus) {
|
96
|
fprintf(stderr, "[!] Failed to open '%s', skipping\n", fname);
|
97
|
perror("opendir");
|
98
|
continue;
|
99
|
}
|
100
|
|
101
|
while (1) {
|
102
|
struct pci_dev dev;
|
103
|
|
104
|
de_bus = readdir(dd_bus);
|
105
|
if (!de_bus)
|
106
|
break;
|
107
|
|
108
|
if (de_bus->d_type != DT_DIR)
|
109
|
continue;
|
110
|
|
111
|
if ((de_bus->d_name[0] < '0') || (de_bus->d_name[1] > '9'))
|
112
|
continue;
|
113
|
|
114
|
snprintf(fname, sizeof(fname), "/sys/devices/%s/%s", de_root->d_name, de_bus->d_name);
|
115
|
if (fill_pci_dev(&dev, fname)) {
|
116
|
fprintf(stderr, "[w] Failed to read info about '%s', skipping\n", fname);
|
117
|
continue;
|
118
|
}
|
119
|
|
120
|
cb(&dev);
|
121
|
}
|
122
|
|
123
|
closedir(dd_bus);
|
124
|
}
|
125
|
|
126
|
closedir(dd_root);
|
127
|
}
|
128
|
|
129
|
static int
|
130
|
patch_device(struct pci_dev *dev)
|
131
|
{
|
132
|
char fname[PATH_MAX];
|
133
|
int fd;
|
134
|
void *mem;
|
135
|
|
136
|
snprintf(fname, sizeof(fname), "%s/resource0", dev->path);
|
137
|
fd = open(fname, O_RDWR);
|
138
|
if (fd < 0) {
|
139
|
fprintf(stderr, "[!] Failed to open resource '%s'\n", fname);
|
140
|
perror("open");
|
141
|
return -1;
|
142
|
}
|
143
|
|
144
|
mem = mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
145
|
if (!mem) {
|
146
|
fprintf(stderr, "[!] Failed to mmmap\n");
|
147
|
perror("mmap");
|
148
|
close(fd);
|
149
|
return -1;
|
150
|
}
|
151
|
|
152
|
volatile uint64_t *HOST_CTRL_BW_MAX_REG = (uint64_t *)(mem + 0x8128);
|
153
|
uint64_t v;
|
154
|
|
155
|
v = *HOST_CTRL_BW_MAX_REG;
|
156
|
|
157
|
printf("[.] Previous HOST_CTRL_BW_MAX_REG value: %016" PRIx64 "\n", v);
|
158
|
|
159
|
v &= 0xfffffff000ffffff;
|
160
|
v |= 0x00000005b0000000; /* Patched value */
|
161
|
// v |= 0x0000000505000000; /* Default value */
|
162
|
|
163
|
printf("[.] Updated HOST_CTRL_BW_MAX_REG value: %016" PRIx64 "\n", v);
|
164
|
|
165
|
*HOST_CTRL_BW_MAX_REG = v;
|
166
|
|
167
|
close(fd);
|
168
|
|
169
|
return 0;
|
170
|
}
|
171
|
|
172
|
static void
|
173
|
scan_cb(struct pci_dev *dev)
|
174
|
{
|
175
|
if ((dev->vendor != 0x8086) || (dev->class != 0x0c0330))
|
176
|
return;
|
177
|
|
178
|
fprintf(stderr, "[+] Found Intel xHCI controller at '%s'\n", dev->path);
|
179
|
patch_device(dev);
|
180
|
}
|
181
|
|
182
|
int main(int argc, char *argv[])
|
183
|
{
|
184
|
if (geteuid() != 0) {
|
185
|
fprintf(stderr, "[!] Please run as root\n");
|
186
|
return -1;
|
187
|
}
|
188
|
|
189
|
if (argc == 1) {
|
190
|
/* No arguments, scan all */
|
191
|
scan_pci_devices(scan_cb);
|
192
|
} else {
|
193
|
struct pci_dev dev;
|
194
|
|
195
|
if (fill_pci_dev(&dev, argv[1])) {
|
196
|
fprintf(stderr, "[!] Failed to read info about '%s'\n", argv[1]);
|
197
|
return -1;
|
198
|
}
|
199
|
|
200
|
if ((dev.vendor != 0x8086) || (dev.class != 0x0c0330)) {
|
201
|
fprintf(stderr, "[!] Not an intel xHCI controller\n");
|
202
|
return -1;
|
203
|
}
|
204
|
|
205
|
patch_device(&dev);
|
206
|
}
|
207
|
}
|