1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2018 Netronome Systems, Inc. */ |
3 | |
4 | #include <linux/bitfield.h> |
5 | #include <linux/device.h> |
6 | #include <linux/kernel.h> |
7 | #include <linux/types.h> |
8 | |
9 | #include "nfp_net_ctrl.h" |
10 | #include "nfp_net.h" |
11 | |
12 | static void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps) |
13 | { |
14 | memset(caps, 0, sizeof(*caps)); |
15 | caps->me_freq_mhz = 1200; |
16 | caps->mbox_off = NFP_NET_CFG_MBOX_BASE; |
17 | caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ; |
18 | } |
19 | |
20 | static bool |
21 | nfp_net_tls_parse_crypto_ops(struct device *dev, struct nfp_net_tlv_caps *caps, |
22 | u8 __iomem *ctrl_mem, u8 __iomem *data, |
23 | unsigned int length, unsigned int offset, |
24 | bool rx_stream_scan) |
25 | { |
26 | /* Ignore the legacy TLV if new one was already parsed */ |
27 | if (caps->tls_resync_ss && !rx_stream_scan) |
28 | return true; |
29 | |
30 | if (length < 32) { |
31 | dev_err(dev, |
32 | "CRYPTO OPS TLV should be at least 32B, is %dB offset:%u\n" , |
33 | length, offset); |
34 | return false; |
35 | } |
36 | |
37 | caps->crypto_ops = readl(addr: data); |
38 | caps->crypto_enable_off = data - ctrl_mem + 16; |
39 | caps->tls_resync_ss = rx_stream_scan; |
40 | |
41 | return true; |
42 | } |
43 | |
44 | int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, |
45 | struct nfp_net_tlv_caps *caps) |
46 | { |
47 | u8 __iomem *data = ctrl_mem + NFP_NET_CFG_TLV_BASE; |
48 | u8 __iomem *end = ctrl_mem + NFP_NET_CFG_BAR_SZ; |
49 | u32 hdr; |
50 | |
51 | nfp_net_tlv_caps_reset(caps); |
52 | |
53 | hdr = readl(addr: data); |
54 | if (!hdr) |
55 | return 0; |
56 | |
57 | while (true) { |
58 | unsigned int length, offset; |
59 | u32 hdr = readl(addr: data); |
60 | |
61 | length = FIELD_GET(NFP_NET_CFG_TLV_HEADER_LENGTH, hdr); |
62 | offset = data - ctrl_mem; |
63 | |
64 | /* Advance past the header */ |
65 | data += 4; |
66 | |
67 | if (length % NFP_NET_CFG_TLV_LENGTH_INC) { |
68 | dev_err(dev, "TLV size not multiple of %u offset:%u len:%u\n" , |
69 | NFP_NET_CFG_TLV_LENGTH_INC, offset, length); |
70 | return -EINVAL; |
71 | } |
72 | if (data + length > end) { |
73 | dev_err(dev, "oversized TLV offset:%u len:%u\n" , |
74 | offset, length); |
75 | return -EINVAL; |
76 | } |
77 | |
78 | switch (FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr)) { |
79 | case NFP_NET_CFG_TLV_TYPE_UNKNOWN: |
80 | dev_err(dev, "NULL TLV at offset:%u\n" , offset); |
81 | return -EINVAL; |
82 | case NFP_NET_CFG_TLV_TYPE_RESERVED: |
83 | break; |
84 | case NFP_NET_CFG_TLV_TYPE_END: |
85 | if (!length) |
86 | return 0; |
87 | |
88 | dev_err(dev, "END TLV should be empty, has offset:%u len:%d\n" , |
89 | offset, length); |
90 | return -EINVAL; |
91 | case NFP_NET_CFG_TLV_TYPE_ME_FREQ: |
92 | if (length != 4) { |
93 | dev_err(dev, |
94 | "ME FREQ TLV should be 4B, is %dB offset:%u\n" , |
95 | length, offset); |
96 | return -EINVAL; |
97 | } |
98 | |
99 | caps->me_freq_mhz = readl(addr: data); |
100 | break; |
101 | case NFP_NET_CFG_TLV_TYPE_MBOX: |
102 | if (!length) { |
103 | caps->mbox_off = 0; |
104 | caps->mbox_len = 0; |
105 | } else { |
106 | caps->mbox_off = data - ctrl_mem; |
107 | caps->mbox_len = length; |
108 | } |
109 | break; |
110 | case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL0: |
111 | case NFP_NET_CFG_TLV_TYPE_EXPERIMENTAL1: |
112 | dev_warn(dev, |
113 | "experimental TLV type:%u offset:%u len:%u\n" , |
114 | FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr), |
115 | offset, length); |
116 | break; |
117 | case NFP_NET_CFG_TLV_TYPE_REPR_CAP: |
118 | if (length < 4) { |
119 | dev_err(dev, "REPR CAP TLV short %dB < 4B offset:%u\n" , |
120 | length, offset); |
121 | return -EINVAL; |
122 | } |
123 | |
124 | caps->repr_cap = readl(addr: data); |
125 | break; |
126 | case NFP_NET_CFG_TLV_TYPE_MBOX_CMSG_TYPES: |
127 | if (length >= 4) |
128 | caps->mbox_cmsg_types = readl(addr: data); |
129 | break; |
130 | case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS: |
131 | if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem, |
132 | data, length, offset, |
133 | rx_stream_scan: false)) |
134 | return -EINVAL; |
135 | break; |
136 | case NFP_NET_CFG_TLV_TYPE_VNIC_STATS: |
137 | if ((data - ctrl_mem) % 8) { |
138 | dev_warn(dev, "VNIC STATS TLV misaligned, ignoring offset:%u len:%u\n" , |
139 | offset, length); |
140 | break; |
141 | } |
142 | caps->vnic_stats_off = data - ctrl_mem; |
143 | caps->vnic_stats_cnt = length / 10; |
144 | break; |
145 | case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN: |
146 | if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem, |
147 | data, length, offset, |
148 | rx_stream_scan: true)) |
149 | return -EINVAL; |
150 | break; |
151 | default: |
152 | if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr)) |
153 | break; |
154 | |
155 | dev_err(dev, "unknown TLV type:%u offset:%u len:%u\n" , |
156 | FIELD_GET(NFP_NET_CFG_TLV_HEADER_TYPE, hdr), |
157 | offset, length); |
158 | return -EINVAL; |
159 | } |
160 | |
161 | data += length; |
162 | if (data + 4 > end) { |
163 | dev_err(dev, "reached end of BAR without END TLV\n" ); |
164 | return -EINVAL; |
165 | } |
166 | } |
167 | |
168 | /* Not reached */ |
169 | return -EINVAL; |
170 | } |
171 | |