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