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