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