xref: /dpdk/lib/pci/rte_pci.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation.
3  * Copyright 2013-2014 6WIND S.A.
4  */
5 
6 #include <string.h>
7 #include <inttypes.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <sys/queue.h>
12 
13 #include <rte_errno.h>
14 #include <rte_interrupts.h>
15 #include <rte_log.h>
16 #include <rte_bus.h>
17 #include <rte_eal_paging.h>
18 #include <rte_per_lcore.h>
19 #include <rte_memory.h>
20 #include <rte_eal.h>
21 #include <rte_string_fns.h>
22 #include <rte_common.h>
23 #include <rte_debug.h>
24 
25 #include "rte_pci.h"
26 
27 static inline const char *
28 get_u8_pciaddr_field(const char *in, void *_u8, char dlm)
29 {
30 	unsigned long val;
31 	uint8_t *u8 = _u8;
32 	char *end;
33 
34 	/* empty string is an error though strtoul() returns 0 */
35 	if (*in == '\0')
36 		return NULL;
37 
38 	/* PCI field starting with spaces is forbidden.
39 	 * Negative wrap-around is not reported as an error by strtoul.
40 	 */
41 	if (*in == ' ' || *in == '-')
42 		return NULL;
43 
44 	errno = 0;
45 	val = strtoul(in, &end, 16);
46 	if (errno != 0 || end[0] != dlm || val > UINT8_MAX) {
47 		errno = errno ? errno : EINVAL;
48 		return NULL;
49 	}
50 	*u8 = (uint8_t)val;
51 	return end + 1;
52 }
53 
54 static int
55 pci_bdf_parse(const char *input, struct rte_pci_addr *dev_addr)
56 {
57 	const char *in = input;
58 
59 	dev_addr->domain = 0;
60 	in = get_u8_pciaddr_field(in, &dev_addr->bus, ':');
61 	if (in == NULL)
62 		return -EINVAL;
63 	in = get_u8_pciaddr_field(in, &dev_addr->devid, '.');
64 	if (in == NULL)
65 		return -EINVAL;
66 	in = get_u8_pciaddr_field(in, &dev_addr->function, '\0');
67 	if (in == NULL)
68 		return -EINVAL;
69 	return 0;
70 }
71 
72 static int
73 pci_dbdf_parse(const char *input, struct rte_pci_addr *dev_addr)
74 {
75 	const char *in = input;
76 	unsigned long val;
77 	char *end;
78 
79 	/* PCI id starting with spaces is forbidden.
80 	 * Negative wrap-around is not reported as an error by strtoul.
81 	 */
82 	if (*in == ' ' || *in == '-')
83 		return -EINVAL;
84 
85 	errno = 0;
86 	val = strtoul(in, &end, 16);
87 	/* Empty string is not an error for strtoul, but the check
88 	 *   end[0] != ':'
89 	 * will detect the issue.
90 	 */
91 	if (errno != 0 || end[0] != ':' || val > UINT32_MAX)
92 		return -EINVAL;
93 	dev_addr->domain = (uint32_t)val;
94 	in = end + 1;
95 	in = get_u8_pciaddr_field(in, &dev_addr->bus, ':');
96 	if (in == NULL)
97 		return -EINVAL;
98 	in = get_u8_pciaddr_field(in, &dev_addr->devid, '.');
99 	if (in == NULL)
100 		return -EINVAL;
101 	in = get_u8_pciaddr_field(in, &dev_addr->function, '\0');
102 	if (in == NULL)
103 		return -EINVAL;
104 	return 0;
105 }
106 
107 void
108 rte_pci_device_name(const struct rte_pci_addr *addr,
109 		char *output, size_t size)
110 {
111 	RTE_VERIFY(size >= PCI_PRI_STR_SIZE);
112 	RTE_VERIFY(snprintf(output, size, PCI_PRI_FMT,
113 			    addr->domain, addr->bus,
114 			    addr->devid, addr->function) >= 0);
115 }
116 
117 int
118 rte_pci_addr_cmp(const struct rte_pci_addr *addr,
119 	     const struct rte_pci_addr *addr2)
120 {
121 	uint64_t dev_addr, dev_addr2;
122 
123 	if ((addr == NULL) || (addr2 == NULL))
124 		return -1;
125 
126 	dev_addr = ((uint64_t)addr->domain << 24) |
127 		(addr->bus << 16) | (addr->devid << 8) | addr->function;
128 	dev_addr2 = ((uint64_t)addr2->domain << 24) |
129 		(addr2->bus << 16) | (addr2->devid << 8) | addr2->function;
130 
131 	if (dev_addr > dev_addr2)
132 		return 1;
133 	else if (dev_addr < dev_addr2)
134 		return -1;
135 	else
136 		return 0;
137 }
138 
139 int
140 rte_pci_addr_parse(const char *str, struct rte_pci_addr *addr)
141 {
142 	if (pci_bdf_parse(str, addr) == 0 ||
143 	    pci_dbdf_parse(str, addr) == 0)
144 		return 0;
145 	return -1;
146 }
147