-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpcredit.c
208 lines (168 loc) · 5.24 KB
/
pcredit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <pci/pci.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/errno.h>
/*
* Generic PCI configuration space registers.
*/
#define REG_VENDOR 0x00
#define REG_DEVICE 0x04
/*
* D31:F1 configuration space registers.
*/
#define REG_P2SB_BAR 0x10
#define REG_P2SB_BARH 0x14
#define REG_P2SB_CTRL 0xe0
#define REG_P2SB_CTRL_HIDE 0x0100
/*
* P2SB private registers.
*/
#define P2SB_PORTID_SHIFT 16
/*
* Helper functions.
*/
#define MSG(...) do { \
fprintf(stderr, "[*] " __VA_ARGS__); fprintf(stderr, "\n"); \
} while(0)
#define ERR(...) do { \
fprintf(stderr, "[-] " __VA_ARGS__); fprintf(stderr, "\n"); \
return 1; \
} while(0)
#define DIE(...) do { *fatal = 1; ERR(__VA_ARGS__) } while(0)
struct pci_dev *pci_find_dev(struct pci_access *pci, uint8_t bus, uint8_t dev, uint8_t func) {
for(struct pci_dev *it = pci->devices; it; it = it->next) {
if(it->bus == bus && it->dev == dev && it->func == func) return it;
}
return NULL;
}
int get_pch_sbreg_addr(struct pci_access *pci, pciaddr_t *sbreg_addr) {
MSG("Checking for a Series 10 PCH system");
struct pci_dev *d31f1 = pci_get_dev(pci, 0, 0, 31, 1);
pci_fill_info(d31f1, PCI_FILL_IDENT);
if(d31f1->vendor_id == 0xffff) {
MSG("Cannot find D31:F1, assuming it is hidden by firmware");
uint32_t p2sb_ctrl = pci_read_long(d31f1, REG_P2SB_CTRL);
MSG("P2SB_CTRL=%02x", p2sb_ctrl);
if(!(p2sb_ctrl & REG_P2SB_CTRL_HIDE)) {
ERR("D31:F1 is hidden but P2SB_E1 is not 0xff, bailing out");
}
MSG("Unhiding P2SB");
pci_write_long(d31f1, REG_P2SB_CTRL, p2sb_ctrl & ~REG_P2SB_CTRL_HIDE);
p2sb_ctrl = pci_read_long(d31f1, REG_P2SB_CTRL);
MSG("P2SB_CTRL=%02x", p2sb_ctrl);
if(p2sb_ctrl & REG_P2SB_CTRL_HIDE) {
ERR("Cannot unhide PS2B");
}
pci_fill_info(d31f1, PCI_FILL_RESCAN | PCI_FILL_IDENT);
if(d31f1->vendor_id == 0xffff) {
ERR("P2SB unhidden but does not enumerate, bailing out");
}
}
pci_fill_info(d31f1, PCI_FILL_RESCAN | PCI_FILL_IDENT | PCI_FILL_BASES);
if(d31f1->vendor_id != 0x8086) {
ERR("Vendor of D31:F1 is not Intel");
} else if((uint32_t)d31f1->base_addr[0] == 0xffffffff) {
ERR("SBREG_BAR is not implemented in D31:F1");
}
*sbreg_addr = d31f1->base_addr[0] &~ 0xf;
MSG("SBREG_ADDR=%08lx", *sbreg_addr);
MSG("Hiding P2SB again");
uint32_t p2sb_ctrl = pci_read_long(d31f1, REG_P2SB_CTRL);
pci_write_long(d31f1, REG_P2SB_CTRL, p2sb_ctrl | REG_P2SB_CTRL_HIDE);
pci_fill_info(d31f1, PCI_FILL_RESCAN | PCI_FILL_IDENT);
if(d31f1->vendor_id != 0xffff) {
ERR("Cannot hide P2SB");
}
return 0;
}
uint32_t sideband_read(void *sbmap, uint8_t port, uint16_t reg) {
volatile uint32_t *addr;
uint32_t val;
addr = (volatile uint32_t *)((uintptr_t)sbmap + (port << P2SB_PORTID_SHIFT) + reg);
val = *addr;
MSG("*%p == %08x", addr, val);
return val;
}
void sideband_write(void *sbmap, uint8_t port, uint16_t reg, uint32_t value) {
volatile uint32_t *addr;
addr = (volatile uint32_t *)((uintptr_t)sbmap + (port << P2SB_PORTID_SHIFT) + reg);
MSG("*%p = %08x", addr, value);
*addr = value;
}
int try_pch(struct pci_access *pci, uint8_t port, uint32_t offset, uint8_t do_write, uint32_t value) {
pciaddr_t sbreg_addr;
if(get_pch_sbreg_addr(pci, &sbreg_addr)) {
MSG("Re-enumerating PCI devices will probably crash the system");
ERR("Probing Series 100 PCH failed");
}
int memfd = open("/dev/mem", O_RDWR);
if(memfd == -1) {
ERR("Cannot open /dev/mem");
}
void *sbmap = mmap((void*)sbreg_addr, 1<<24, PROT_READ|PROT_WRITE, MAP_SHARED,
memfd, sbreg_addr);
if(sbmap == MAP_FAILED) {
if(errno == EPERM) {
// The requirement might be relaxed to CONFIG_IO_DEVMEM_STRICT=n, but I'm not sure.
MSG("Is your kernel configured with CONFIG_DEVMEM_STRICT=n?");
}
ERR("Cannot map SBREG");
}
close(memfd);
sideband_read(sbmap, port, offset);
if (do_write) {
sideband_write(sbmap, port, offset, value);
sideband_read(sbmap, port, offset);
}
return 0;
}
int create_pci(int method, struct pci_access **pci_out) {
struct pci_access *pci = pci_alloc();
pci->method = method;
pci_init(pci);
pci_scan_bus(pci);
struct pci_dev *d31f0 = pci_find_dev(pci, 0, 31, 0);
if(!d31f0) {
ERR("Cannot find D31:F0");
}
pci_fill_info(d31f0, PCI_FILL_IDENT | PCI_FILL_BASES);
if(d31f0->vendor_id != 0x8086) {
ERR("Vendor of D31:F0 is not Intel");
}
*pci_out = pci;
return 0;
}
int main(int argc, char **argv) {
struct pci_access *pci;
uint32_t port;
uint32_t offset;
uint8_t do_write = 0;
uint32_t value = 0;
if (argc != 3 && argc != 4) {
printf("Usage: %s <port> <register offset (in bytes)> [value to write]\n", argv[0]);
return 1;
}
port = strtoul(argv[1], NULL, 16);
offset = strtoul(argv[2], NULL, 16);
if (argc > 3) {
do_write = 1;
value = strtoul(argv[3], NULL, 16);
}
if(create_pci(PCI_ACCESS_AUTO, &pci)) {
MSG("Is this an Intel platform?");
return 1;
}
if(create_pci(PCI_ACCESS_I386_TYPE1, &pci)) {
return 1;
}
if(try_pch(pci, port, offset, do_write, value)) {
return 1;
}
printf("[+] Done\n");
return 0;
}