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 *
get_u8_pciaddr_field(const char * in,void * _u8,char dlm)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
pci_bdf_parse(const char * input,struct rte_pci_addr * dev_addr)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
pci_dbdf_parse(const char * input,struct rte_pci_addr * dev_addr)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
rte_pci_device_name(const struct rte_pci_addr * addr,char * output,size_t size)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
rte_pci_addr_cmp(const struct rte_pci_addr * addr,const struct rte_pci_addr * addr2)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
rte_pci_addr_parse(const char * str,struct rte_pci_addr * addr)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