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