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