1 /*
2 * The PCI Library -- Direct Configuration access via memory mapped ports
3 *
4 * Copyright (c) 2022 Pali Rohár <[email protected]>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "internal.h"
12 #include "physmem.h"
13 #include "physmem-access.h"
14
15 #include <ctype.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 struct mmio_cache {
21 u64 addr_page;
22 u64 data_page;
23 void *addr_map;
24 void *data_map;
25 };
26
27 struct mmio_access {
28 struct mmio_cache *cache;
29 struct physmem *physmem;
30 long pagesize;
31 };
32
33 static void
munmap_regs(struct pci_access * a)34 munmap_regs(struct pci_access *a)
35 {
36 struct mmio_access *macc = a->backend_data;
37 struct mmio_cache *cache = macc->cache;
38 struct physmem *physmem = macc->physmem;
39 long pagesize = macc->pagesize;
40
41 if (!cache)
42 return;
43
44 physmem_unmap(physmem, cache->addr_map, pagesize);
45 if (cache->addr_page != cache->data_page)
46 physmem_unmap(physmem, cache->data_map, pagesize);
47
48 pci_mfree(macc->cache);
49 macc->cache = NULL;
50 }
51
52 static int
mmap_regs(struct pci_access * a,u64 addr_reg,u64 data_reg,int data_off,volatile void ** addr,volatile void ** data)53 mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data)
54 {
55 struct mmio_access *macc = a->backend_data;
56 struct mmio_cache *cache = macc->cache;
57 struct physmem *physmem = macc->physmem;
58 long pagesize = macc->pagesize;
59 u64 addr_page = addr_reg & ~(pagesize-1);
60 u64 data_page = data_reg & ~(pagesize-1);
61 void *addr_map = (void *)-1;
62 void *data_map = (void *)-1;
63
64 if (cache && cache->addr_page == addr_page)
65 addr_map = cache->addr_map;
66
67 if (cache && cache->data_page == data_page)
68 data_map = cache->data_map;
69
70 if (addr_map == (void *)-1)
71 addr_map = physmem_map(physmem, addr_page, pagesize, 1);
72
73 if (addr_map == (void *)-1)
74 return 0;
75
76 if (data_map == (void *)-1)
77 {
78 if (data_page == addr_page)
79 data_map = addr_map;
80 else
81 data_map = physmem_map(physmem, data_page, pagesize, 1);
82 }
83
84 if (data_map == (void *)-1)
85 {
86 if (!cache || cache->addr_map != addr_map)
87 physmem_unmap(physmem, addr_map, pagesize);
88 return 0;
89 }
90
91 if (cache && cache->addr_page != addr_page)
92 physmem_unmap(physmem, cache->addr_map, pagesize);
93
94 if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page)
95 physmem_unmap(physmem, cache->data_map, pagesize);
96
97 if (!cache)
98 cache = macc->cache = pci_malloc(a, sizeof(*cache));
99
100 cache->addr_page = addr_page;
101 cache->data_page = data_page;
102 cache->addr_map = addr_map;
103 cache->data_map = data_map;
104
105 *addr = (unsigned char *)addr_map + (addr_reg & (pagesize-1));
106 *data = (unsigned char *)data_map + (data_reg & (pagesize-1)) + data_off;
107 return 1;
108 }
109
110 static int
validate_addrs(const char * addrs)111 validate_addrs(const char *addrs)
112 {
113 const char *sep, *next;
114 u64 num;
115 char *endptr;
116
117 if (!*addrs)
118 return 0;
119
120 while (1)
121 {
122 next = strchr(addrs, ',');
123 if (!next)
124 next = addrs + strlen(addrs);
125
126 sep = strchr(addrs, '/');
127 if (!sep)
128 return 0;
129
130 if (!isxdigit(*addrs) || !isxdigit(*(sep+1)))
131 return 0;
132
133 errno = 0;
134 num = strtoull(addrs, &endptr, 16);
135 if (errno || endptr != sep || (num & 3))
136 return 0;
137
138 errno = 0;
139 num = strtoull(sep+1, &endptr, 16);
140 if (errno || endptr != next || (num & 3))
141 return 0;
142
143 if (!*next)
144 return 1;
145
146 addrs = next + 1;
147 }
148 }
149
150 static int
get_domain_count(const char * addrs)151 get_domain_count(const char *addrs)
152 {
153 int count = 1;
154 while (addrs = strchr(addrs, ','))
155 {
156 addrs++;
157 count++;
158 }
159 return count;
160 }
161
162 static int
get_domain_addr(const char * addrs,int domain,u64 * addr_reg,u64 * data_reg)163 get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg)
164 {
165 char *endptr;
166
167 while (domain-- > 0)
168 {
169 addrs = strchr(addrs, ',');
170 if (!addrs)
171 return 0;
172 addrs++;
173 }
174
175 *addr_reg = strtoull(addrs, &endptr, 16);
176 *data_reg = strtoull(endptr+1, NULL, 16);
177
178 return 1;
179 }
180
181 static void
conf1_config(struct pci_access * a)182 conf1_config(struct pci_access *a)
183 {
184 physmem_init_config(a);
185 pci_define_param(a, "mmio-conf1.addrs", "", "Physical addresses of memory mapped Intel conf1 interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
186 }
187
188 static void
conf1_ext_config(struct pci_access * a)189 conf1_ext_config(struct pci_access *a)
190 {
191 physmem_init_config(a);
192 pci_define_param(a, "mmio-conf1-ext.addrs", "", "Physical addresses of memory mapped Intel conf1 extended interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
193 }
194
195 static int
detect(struct pci_access * a,char * addrs_param_name)196 detect(struct pci_access *a, char *addrs_param_name)
197 {
198 char *addrs = pci_get_param(a, addrs_param_name);
199
200 if (!*addrs)
201 {
202 a->debug("%s was not specified", addrs_param_name);
203 return 0;
204 }
205
206 if (!validate_addrs(addrs))
207 {
208 a->debug("%s has invalid address format %s", addrs_param_name, addrs);
209 return 0;
210 }
211
212 if (physmem_access(a, 1))
213 {
214 a->debug("cannot access physical memory: %s", strerror(errno));
215 return 0;
216 }
217
218 a->debug("using with %s", addrs);
219 return 1;
220 }
221
222 static int
conf1_detect(struct pci_access * a)223 conf1_detect(struct pci_access *a)
224 {
225 return detect(a, "mmio-conf1.addrs");
226 }
227
228 static int
conf1_ext_detect(struct pci_access * a)229 conf1_ext_detect(struct pci_access *a)
230 {
231 return detect(a, "mmio-conf1-ext.addrs");
232 }
233
234 static char*
get_addrs_param_name(struct pci_access * a)235 get_addrs_param_name(struct pci_access *a)
236 {
237 if (a->methods->config == conf1_ext_config)
238 return "mmio-conf1-ext.addrs";
239 else
240 return "mmio-conf1.addrs";
241 }
242
243 static void
conf1_init(struct pci_access * a)244 conf1_init(struct pci_access *a)
245 {
246 char *addrs_param_name = get_addrs_param_name(a);
247 char *addrs = pci_get_param(a, addrs_param_name);
248 struct mmio_access *macc;
249 struct physmem *physmem;
250 long pagesize;
251
252 if (!*addrs)
253 a->error("Option %s was not specified.", addrs_param_name);
254
255 if (!validate_addrs(addrs))
256 a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs);
257
258 physmem = physmem_open(a, 1);
259 if (!physmem)
260 a->error("Cannot open physcal memory: %s.", strerror(errno));
261
262 pagesize = physmem_get_pagesize(physmem);
263 if (pagesize <= 0)
264 a->error("Cannot get page size: %s.", strerror(errno));
265
266 macc = pci_malloc(a, sizeof(*macc));
267 macc->cache = NULL;
268 macc->physmem = physmem;
269 macc->pagesize = pagesize;
270 a->backend_data = macc;
271 }
272
273 static void
conf1_cleanup(struct pci_access * a)274 conf1_cleanup(struct pci_access *a)
275 {
276 struct mmio_access *macc = a->backend_data;
277
278 munmap_regs(a);
279 physmem_close(macc->physmem);
280 pci_mfree(macc);
281 }
282
283 static void
conf1_scan(struct pci_access * a)284 conf1_scan(struct pci_access *a)
285 {
286 char *addrs_param_name = get_addrs_param_name(a);
287 char *addrs = pci_get_param(a, addrs_param_name);
288 int domain_count = get_domain_count(addrs);
289 int domain;
290
291 for (domain = 0; domain < domain_count; domain++)
292 pci_generic_scan_domain(a, domain);
293 }
294
295 static int
conf1_ext_read(struct pci_dev * d,int pos,byte * buf,int len)296 conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len)
297 {
298 char *addrs_param_name = get_addrs_param_name(d->access);
299 char *addrs = pci_get_param(d->access, addrs_param_name);
300 volatile void *addr, *data;
301 u64 addr_reg, data_reg;
302
303 if (pos >= 4096)
304 return 0;
305
306 if (len != 1 && len != 2 && len != 4)
307 return pci_generic_block_read(d, pos, buf, len);
308
309 if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
310 return 0;
311
312 if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
313 return 0;
314
315 physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
316 physmem_readl(addr); /* write barrier for address */
317
318 switch (len)
319 {
320 case 1:
321 buf[0] = physmem_readb(data);
322 break;
323 case 2:
324 ((u16 *) buf)[0] = physmem_readw(data);
325 break;
326 case 4:
327 ((u32 *) buf)[0] = physmem_readl(data);
328 break;
329 }
330
331 return 1;
332 }
333
334 static int
conf1_read(struct pci_dev * d,int pos,byte * buf,int len)335 conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
336 {
337 if (pos >= 256)
338 return 0;
339
340 return conf1_ext_read(d, pos, buf, len);
341 }
342
343 static int
conf1_ext_write(struct pci_dev * d,int pos,byte * buf,int len)344 conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len)
345 {
346 char *addrs_param_name = get_addrs_param_name(d->access);
347 char *addrs = pci_get_param(d->access, addrs_param_name);
348 volatile void *addr, *data;
349 u64 addr_reg, data_reg;
350
351 if (pos >= 4096)
352 return 0;
353
354 if (len != 1 && len != 2 && len != 4)
355 return pci_generic_block_write(d, pos, buf, len);
356
357 if (!get_domain_addr(addrs, d->domain, &addr_reg, &data_reg))
358 return 0;
359
360 if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
361 return 0;
362
363 physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
364 physmem_readl(addr); /* write barrier for address */
365
366 switch (len)
367 {
368 case 1:
369 physmem_writeb(buf[0], data);
370 break;
371 case 2:
372 physmem_writew(((u16 *) buf)[0], data);
373 break;
374 case 4:
375 physmem_writel(((u32 *) buf)[0], data);
376 break;
377 }
378
379 /*
380 * write barrier for data
381 * Note that we cannot read from data port because it may have side effect.
382 * Instead we read from address port (which should not have side effect) to
383 * create a barrier between two conf1_write() calls. But this does not have
384 * to be 100% correct as it does not ensure barrier on data port itself.
385 * Correct way is to issue CPU instruction for full hw sync barrier but gcc
386 * does not provide any (builtin) function yet.
387 */
388 physmem_readl(addr);
389
390 return 1;
391 }
392
393 static int
conf1_write(struct pci_dev * d,int pos,byte * buf,int len)394 conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
395 {
396 if (pos >= 256)
397 return 0;
398
399 return conf1_ext_write(d, pos, buf, len);
400 }
401
402 struct pci_methods pm_mmio_conf1 = {
403 .name = "mmio-conf1",
404 .help = "Raw memory mapped I/O port access using Intel conf1 interface",
405 .config = conf1_config,
406 .detect = conf1_detect,
407 .init = conf1_init,
408 .cleanup = conf1_cleanup,
409 .scan = conf1_scan,
410 .fill_info = pci_generic_fill_info,
411 .read = conf1_read,
412 .write = conf1_write,
413 };
414
415 struct pci_methods pm_mmio_conf1_ext = {
416 .name = "mmio-conf1-ext",
417 .help = "Raw memory mapped I/O port access using Intel conf1 extended interface",
418 .config = conf1_ext_config,
419 .detect = conf1_ext_detect,
420 .init = conf1_init,
421 .cleanup = conf1_cleanup,
422 .scan = conf1_scan,
423 .fill_info = pci_generic_fill_info,
424 .read = conf1_ext_read,
425 .write = conf1_ext_write,
426 };
427