1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2025, Intel Corporation. */ |
3 | |
4 | #include "ixgbe.h" |
5 | #include "devlink.h" |
6 | |
7 | #define IXGBE_DEVLINK_READ_BLK_SIZE (1024 * 1024) |
8 | |
9 | static const struct devlink_region_ops ixgbe_nvm_region_ops; |
10 | static const struct devlink_region_ops ixgbe_sram_region_ops; |
11 | |
12 | static int ixgbe_devlink_parse_region(struct ixgbe_hw *hw, |
13 | const struct devlink_region_ops *ops, |
14 | bool *read_shadow_ram, u32 *nvm_size) |
15 | { |
16 | if (ops == &ixgbe_nvm_region_ops) { |
17 | *read_shadow_ram = false; |
18 | *nvm_size = hw->flash.flash_size; |
19 | } else if (ops == &ixgbe_sram_region_ops) { |
20 | *read_shadow_ram = true; |
21 | *nvm_size = hw->flash.sr_words * 2u; |
22 | } else { |
23 | return -EOPNOTSUPP; |
24 | } |
25 | |
26 | return 0; |
27 | } |
28 | |
29 | /** |
30 | * ixgbe_devlink_nvm_snapshot - Capture a snapshot of the NVM content |
31 | * @devlink: the devlink instance |
32 | * @ops: the devlink region being snapshotted |
33 | * @extack: extended ACK response structure |
34 | * @data: on exit points to snapshot data buffer |
35 | * |
36 | * This function is called in response to the DEVLINK_CMD_REGION_NEW cmd. |
37 | * |
38 | * Capture a snapshot of the whole requested NVM region. |
39 | * |
40 | * No need to worry with freeing @data, devlink core takes care if it. |
41 | * |
42 | * Return: 0 on success, -EOPNOTSUPP for unsupported regions, -EBUSY when |
43 | * cannot lock NVM, -ENOMEM when cannot alloc mem and -EIO when error |
44 | * occurs during reading. |
45 | */ |
46 | static int ixgbe_devlink_nvm_snapshot(struct devlink *devlink, |
47 | const struct devlink_region_ops *ops, |
48 | struct netlink_ext_ack *extack, u8 **data) |
49 | { |
50 | struct ixgbe_adapter *adapter = devlink_priv(devlink); |
51 | struct ixgbe_hw *hw = &adapter->hw; |
52 | bool read_shadow_ram; |
53 | u8 *nvm_data, *buf; |
54 | u32 nvm_size, left; |
55 | u8 num_blks; |
56 | int err; |
57 | |
58 | err = ixgbe_devlink_parse_region(hw, ops, read_shadow_ram: &read_shadow_ram, nvm_size: &nvm_size); |
59 | if (err) |
60 | return err; |
61 | |
62 | nvm_data = kvzalloc(nvm_size, GFP_KERNEL); |
63 | if (!nvm_data) |
64 | return -ENOMEM; |
65 | |
66 | num_blks = DIV_ROUND_UP(nvm_size, IXGBE_DEVLINK_READ_BLK_SIZE); |
67 | buf = nvm_data; |
68 | left = nvm_size; |
69 | |
70 | for (int i = 0; i < num_blks; i++) { |
71 | u32 read_sz = min_t(u32, IXGBE_DEVLINK_READ_BLK_SIZE, left); |
72 | |
73 | /* Need to acquire NVM lock during each loop run because the |
74 | * total period of reading whole NVM is longer than the maximum |
75 | * period the lock can be taken defined by the IXGBE_NVM_TIMEOUT. |
76 | */ |
77 | err = ixgbe_acquire_nvm(hw, access: IXGBE_RES_READ); |
78 | if (err) { |
79 | NL_SET_ERR_MSG_MOD(extack, |
80 | "Failed to acquire NVM semaphore" ); |
81 | kvfree(addr: nvm_data); |
82 | return -EBUSY; |
83 | } |
84 | |
85 | err = ixgbe_read_flat_nvm(hw, offset: i * IXGBE_DEVLINK_READ_BLK_SIZE, |
86 | length: &read_sz, data: buf, read_shadow_ram); |
87 | if (err) { |
88 | NL_SET_ERR_MSG_MOD(extack, |
89 | "Failed to read RAM content" ); |
90 | ixgbe_release_nvm(hw); |
91 | kvfree(addr: nvm_data); |
92 | return -EIO; |
93 | } |
94 | |
95 | ixgbe_release_nvm(hw); |
96 | |
97 | buf += read_sz; |
98 | left -= read_sz; |
99 | } |
100 | |
101 | *data = nvm_data; |
102 | return 0; |
103 | } |
104 | |
105 | /** |
106 | * ixgbe_devlink_devcaps_snapshot - Capture a snapshot of device capabilities |
107 | * @devlink: the devlink instance |
108 | * @ops: the devlink region being snapshotted |
109 | * @extack: extended ACK response structure |
110 | * @data: on exit points to snapshot data buffer |
111 | * |
112 | * This function is called in response to the DEVLINK_CMD_REGION_NEW for |
113 | * the device-caps devlink region. |
114 | * |
115 | * Capture a snapshot of the device capabilities reported by firmware. |
116 | * |
117 | * No need to worry with freeing @data, devlink core takes care if it. |
118 | * |
119 | * Return: 0 on success, -ENOMEM when cannot alloc mem, or return code of |
120 | * the reading operation. |
121 | */ |
122 | static int ixgbe_devlink_devcaps_snapshot(struct devlink *devlink, |
123 | const struct devlink_region_ops *ops, |
124 | struct netlink_ext_ack *extack, |
125 | u8 **data) |
126 | { |
127 | struct ixgbe_adapter *adapter = devlink_priv(devlink); |
128 | struct ixgbe_aci_cmd_list_caps_elem *caps; |
129 | struct ixgbe_hw *hw = &adapter->hw; |
130 | int err; |
131 | |
132 | caps = kvzalloc(IXGBE_ACI_MAX_BUFFER_SIZE, GFP_KERNEL); |
133 | if (!caps) |
134 | return -ENOMEM; |
135 | |
136 | err = ixgbe_aci_list_caps(hw, buf: caps, IXGBE_ACI_MAX_BUFFER_SIZE, NULL, |
137 | opc: ixgbe_aci_opc_list_dev_caps); |
138 | if (err) { |
139 | NL_SET_ERR_MSG_MOD(extack, |
140 | "Failed to read device capabilities" ); |
141 | kvfree(addr: caps); |
142 | return err; |
143 | } |
144 | |
145 | *data = (u8 *)caps; |
146 | return 0; |
147 | } |
148 | |
149 | /** |
150 | * ixgbe_devlink_nvm_read - Read a portion of NVM flash content |
151 | * @devlink: the devlink instance |
152 | * @ops: the devlink region to snapshot |
153 | * @extack: extended ACK response structure |
154 | * @offset: the offset to start at |
155 | * @size: the amount to read |
156 | * @data: the data buffer to read into |
157 | * |
158 | * This function is called in response to DEVLINK_CMD_REGION_READ to directly |
159 | * read a section of the NVM contents. |
160 | * |
161 | * Read from either the nvm-flash region either shadow-ram region. |
162 | * |
163 | * Return: 0 on success, -EOPNOTSUPP for unsupported regions, -EBUSY when |
164 | * cannot lock NVM, -ERANGE when buffer limit exceeded and -EIO when error |
165 | * occurs during reading. |
166 | */ |
167 | static int ixgbe_devlink_nvm_read(struct devlink *devlink, |
168 | const struct devlink_region_ops *ops, |
169 | struct netlink_ext_ack *extack, |
170 | u64 offset, u32 size, u8 *data) |
171 | { |
172 | struct ixgbe_adapter *adapter = devlink_priv(devlink); |
173 | struct ixgbe_hw *hw = &adapter->hw; |
174 | bool read_shadow_ram; |
175 | u32 nvm_size; |
176 | int err; |
177 | |
178 | err = ixgbe_devlink_parse_region(hw, ops, read_shadow_ram: &read_shadow_ram, nvm_size: &nvm_size); |
179 | if (err) |
180 | return err; |
181 | |
182 | if (offset + size > nvm_size) { |
183 | NL_SET_ERR_MSG_MOD(extack, "Cannot read beyond the region size" ); |
184 | return -ERANGE; |
185 | } |
186 | |
187 | err = ixgbe_acquire_nvm(hw, access: IXGBE_RES_READ); |
188 | if (err) { |
189 | NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore" ); |
190 | return -EBUSY; |
191 | } |
192 | |
193 | err = ixgbe_read_flat_nvm(hw, offset: (u32)offset, length: &size, data, read_shadow_ram); |
194 | if (err) { |
195 | NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents" ); |
196 | ixgbe_release_nvm(hw); |
197 | return -EIO; |
198 | } |
199 | |
200 | ixgbe_release_nvm(hw); |
201 | return 0; |
202 | } |
203 | |
204 | static const struct devlink_region_ops ixgbe_nvm_region_ops = { |
205 | .name = "nvm-flash" , |
206 | .destructor = kvfree, |
207 | .snapshot = ixgbe_devlink_nvm_snapshot, |
208 | .read = ixgbe_devlink_nvm_read, |
209 | }; |
210 | |
211 | static const struct devlink_region_ops ixgbe_sram_region_ops = { |
212 | .name = "shadow-ram" , |
213 | .destructor = kvfree, |
214 | .snapshot = ixgbe_devlink_nvm_snapshot, |
215 | .read = ixgbe_devlink_nvm_read, |
216 | }; |
217 | |
218 | static const struct devlink_region_ops ixgbe_devcaps_region_ops = { |
219 | .name = "device-caps" , |
220 | .destructor = kvfree, |
221 | .snapshot = ixgbe_devlink_devcaps_snapshot, |
222 | }; |
223 | |
224 | /** |
225 | * ixgbe_devlink_init_regions - Initialize devlink regions |
226 | * @adapter: adapter instance |
227 | * |
228 | * Create devlink regions used to enable access to dump the contents of the |
229 | * flash memory of the device. |
230 | */ |
231 | void ixgbe_devlink_init_regions(struct ixgbe_adapter *adapter) |
232 | { |
233 | struct devlink *devlink = adapter->devlink; |
234 | struct device *dev = &adapter->pdev->dev; |
235 | u64 nvm_size, sram_size; |
236 | |
237 | if (adapter->hw.mac.type != ixgbe_mac_e610) |
238 | return; |
239 | |
240 | nvm_size = adapter->hw.flash.flash_size; |
241 | adapter->nvm_region = devl_region_create(devlink, ops: &ixgbe_nvm_region_ops, |
242 | region_max_snapshots: 1, region_size: nvm_size); |
243 | if (IS_ERR(ptr: adapter->nvm_region)) { |
244 | dev_err(dev, |
245 | "Failed to create NVM devlink region, err %ld\n" , |
246 | PTR_ERR(adapter->nvm_region)); |
247 | adapter->nvm_region = NULL; |
248 | } |
249 | |
250 | sram_size = adapter->hw.flash.sr_words * 2u; |
251 | adapter->sram_region = devl_region_create(devlink, ops: &ixgbe_sram_region_ops, |
252 | region_max_snapshots: 1, region_size: sram_size); |
253 | if (IS_ERR(ptr: adapter->sram_region)) { |
254 | dev_err(dev, |
255 | "Failed to create shadow-ram devlink region, err %ld\n" , |
256 | PTR_ERR(adapter->sram_region)); |
257 | adapter->sram_region = NULL; |
258 | } |
259 | |
260 | adapter->devcaps_region = devl_region_create(devlink, |
261 | ops: &ixgbe_devcaps_region_ops, |
262 | region_max_snapshots: 10, IXGBE_ACI_MAX_BUFFER_SIZE); |
263 | if (IS_ERR(ptr: adapter->devcaps_region)) { |
264 | dev_err(dev, |
265 | "Failed to create device-caps devlink region, err %ld\n" , |
266 | PTR_ERR(adapter->devcaps_region)); |
267 | adapter->devcaps_region = NULL; |
268 | } |
269 | } |
270 | |
271 | /** |
272 | * ixgbe_devlink_destroy_regions - Destroy devlink regions |
273 | * @adapter: adapter instance |
274 | * |
275 | * Remove previously created regions for this adapter instance. |
276 | */ |
277 | void ixgbe_devlink_destroy_regions(struct ixgbe_adapter *adapter) |
278 | { |
279 | if (adapter->hw.mac.type != ixgbe_mac_e610) |
280 | return; |
281 | |
282 | if (adapter->nvm_region) |
283 | devl_region_destroy(region: adapter->nvm_region); |
284 | |
285 | if (adapter->sram_region) |
286 | devl_region_destroy(region: adapter->sram_region); |
287 | |
288 | if (adapter->devcaps_region) |
289 | devl_region_destroy(region: adapter->devcaps_region); |
290 | } |
291 | |