199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation.
399a2dd95SBruce Richardson * Copyright 2013-2014 6WIND S.A.
499a2dd95SBruce Richardson */
599a2dd95SBruce Richardson
6*72b452c5SDmitry Kozlyuk #include <errno.h>
799a2dd95SBruce Richardson #include <stdint.h>
899a2dd95SBruce Richardson #include <stdlib.h>
999a2dd95SBruce Richardson #include <stdio.h>
1099a2dd95SBruce Richardson
1199a2dd95SBruce Richardson #include <rte_debug.h>
1299a2dd95SBruce Richardson
1399a2dd95SBruce Richardson #include "rte_pci.h"
1499a2dd95SBruce Richardson
1599a2dd95SBruce Richardson static inline const char *
get_u8_pciaddr_field(const char * in,void * _u8,char dlm)1699a2dd95SBruce Richardson get_u8_pciaddr_field(const char *in, void *_u8, char dlm)
1799a2dd95SBruce Richardson {
1899a2dd95SBruce Richardson unsigned long val;
1999a2dd95SBruce Richardson uint8_t *u8 = _u8;
2099a2dd95SBruce Richardson char *end;
2199a2dd95SBruce Richardson
2299a2dd95SBruce Richardson /* empty string is an error though strtoul() returns 0 */
2399a2dd95SBruce Richardson if (*in == '\0')
2499a2dd95SBruce Richardson return NULL;
2599a2dd95SBruce Richardson
2699a2dd95SBruce Richardson /* PCI field starting with spaces is forbidden.
2799a2dd95SBruce Richardson * Negative wrap-around is not reported as an error by strtoul.
2899a2dd95SBruce Richardson */
2999a2dd95SBruce Richardson if (*in == ' ' || *in == '-')
3099a2dd95SBruce Richardson return NULL;
3199a2dd95SBruce Richardson
3299a2dd95SBruce Richardson errno = 0;
3399a2dd95SBruce Richardson val = strtoul(in, &end, 16);
3499a2dd95SBruce Richardson if (errno != 0 || end[0] != dlm || val > UINT8_MAX) {
3599a2dd95SBruce Richardson errno = errno ? errno : EINVAL;
3699a2dd95SBruce Richardson return NULL;
3799a2dd95SBruce Richardson }
3899a2dd95SBruce Richardson *u8 = (uint8_t)val;
3999a2dd95SBruce Richardson return end + 1;
4099a2dd95SBruce Richardson }
4199a2dd95SBruce Richardson
4299a2dd95SBruce Richardson static int
pci_bdf_parse(const char * input,struct rte_pci_addr * dev_addr)4399a2dd95SBruce Richardson pci_bdf_parse(const char *input, struct rte_pci_addr *dev_addr)
4499a2dd95SBruce Richardson {
4599a2dd95SBruce Richardson const char *in = input;
4699a2dd95SBruce Richardson
4799a2dd95SBruce Richardson dev_addr->domain = 0;
4899a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->bus, ':');
4999a2dd95SBruce Richardson if (in == NULL)
5099a2dd95SBruce Richardson return -EINVAL;
5199a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->devid, '.');
5299a2dd95SBruce Richardson if (in == NULL)
5399a2dd95SBruce Richardson return -EINVAL;
5499a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->function, '\0');
5599a2dd95SBruce Richardson if (in == NULL)
5699a2dd95SBruce Richardson return -EINVAL;
5799a2dd95SBruce Richardson return 0;
5899a2dd95SBruce Richardson }
5999a2dd95SBruce Richardson
6099a2dd95SBruce Richardson static int
pci_dbdf_parse(const char * input,struct rte_pci_addr * dev_addr)6199a2dd95SBruce Richardson pci_dbdf_parse(const char *input, struct rte_pci_addr *dev_addr)
6299a2dd95SBruce Richardson {
6399a2dd95SBruce Richardson const char *in = input;
6499a2dd95SBruce Richardson unsigned long val;
6599a2dd95SBruce Richardson char *end;
6699a2dd95SBruce Richardson
6799a2dd95SBruce Richardson /* PCI id starting with spaces is forbidden.
6899a2dd95SBruce Richardson * Negative wrap-around is not reported as an error by strtoul.
6999a2dd95SBruce Richardson */
7099a2dd95SBruce Richardson if (*in == ' ' || *in == '-')
7199a2dd95SBruce Richardson return -EINVAL;
7299a2dd95SBruce Richardson
7399a2dd95SBruce Richardson errno = 0;
7499a2dd95SBruce Richardson val = strtoul(in, &end, 16);
7599a2dd95SBruce Richardson /* Empty string is not an error for strtoul, but the check
7699a2dd95SBruce Richardson * end[0] != ':'
7799a2dd95SBruce Richardson * will detect the issue.
7899a2dd95SBruce Richardson */
7999a2dd95SBruce Richardson if (errno != 0 || end[0] != ':' || val > UINT32_MAX)
8099a2dd95SBruce Richardson return -EINVAL;
8199a2dd95SBruce Richardson dev_addr->domain = (uint32_t)val;
8299a2dd95SBruce Richardson in = end + 1;
8399a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->bus, ':');
8499a2dd95SBruce Richardson if (in == NULL)
8599a2dd95SBruce Richardson return -EINVAL;
8699a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->devid, '.');
8799a2dd95SBruce Richardson if (in == NULL)
8899a2dd95SBruce Richardson return -EINVAL;
8999a2dd95SBruce Richardson in = get_u8_pciaddr_field(in, &dev_addr->function, '\0');
9099a2dd95SBruce Richardson if (in == NULL)
9199a2dd95SBruce Richardson return -EINVAL;
9299a2dd95SBruce Richardson return 0;
9399a2dd95SBruce Richardson }
9499a2dd95SBruce Richardson
9599a2dd95SBruce Richardson void
rte_pci_device_name(const struct rte_pci_addr * addr,char * output,size_t size)9699a2dd95SBruce Richardson rte_pci_device_name(const struct rte_pci_addr *addr,
9799a2dd95SBruce Richardson char *output, size_t size)
9899a2dd95SBruce Richardson {
9999a2dd95SBruce Richardson RTE_VERIFY(size >= PCI_PRI_STR_SIZE);
10099a2dd95SBruce Richardson RTE_VERIFY(snprintf(output, size, PCI_PRI_FMT,
10199a2dd95SBruce Richardson addr->domain, addr->bus,
10299a2dd95SBruce Richardson addr->devid, addr->function) >= 0);
10399a2dd95SBruce Richardson }
10499a2dd95SBruce Richardson
10599a2dd95SBruce Richardson int
rte_pci_addr_cmp(const struct rte_pci_addr * addr,const struct rte_pci_addr * addr2)10699a2dd95SBruce Richardson rte_pci_addr_cmp(const struct rte_pci_addr *addr,
10799a2dd95SBruce Richardson const struct rte_pci_addr *addr2)
10899a2dd95SBruce Richardson {
10999a2dd95SBruce Richardson uint64_t dev_addr, dev_addr2;
11099a2dd95SBruce Richardson
11199a2dd95SBruce Richardson if ((addr == NULL) || (addr2 == NULL))
11299a2dd95SBruce Richardson return -1;
11399a2dd95SBruce Richardson
11499a2dd95SBruce Richardson dev_addr = ((uint64_t)addr->domain << 24) |
11599a2dd95SBruce Richardson (addr->bus << 16) | (addr->devid << 8) | addr->function;
11699a2dd95SBruce Richardson dev_addr2 = ((uint64_t)addr2->domain << 24) |
11799a2dd95SBruce Richardson (addr2->bus << 16) | (addr2->devid << 8) | addr2->function;
11899a2dd95SBruce Richardson
11999a2dd95SBruce Richardson if (dev_addr > dev_addr2)
12099a2dd95SBruce Richardson return 1;
12199a2dd95SBruce Richardson else if (dev_addr < dev_addr2)
12299a2dd95SBruce Richardson return -1;
12399a2dd95SBruce Richardson else
12499a2dd95SBruce Richardson return 0;
12599a2dd95SBruce Richardson }
12699a2dd95SBruce Richardson
12799a2dd95SBruce Richardson int
rte_pci_addr_parse(const char * str,struct rte_pci_addr * addr)12899a2dd95SBruce Richardson rte_pci_addr_parse(const char *str, struct rte_pci_addr *addr)
12999a2dd95SBruce Richardson {
13099a2dd95SBruce Richardson if (pci_bdf_parse(str, addr) == 0 ||
13199a2dd95SBruce Richardson pci_dbdf_parse(str, addr) == 0)
13299a2dd95SBruce Richardson return 0;
13399a2dd95SBruce Richardson return -1;
13499a2dd95SBruce Richardson }
135