1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright(c) 2013 - 2018 Intel Corporation. */ 3 4 #include <linux/firmware.h> 5 #include "i40e.h" 6 7 #define I40_DDP_FLASH_REGION 100 8 #define I40E_PROFILE_INFO_SIZE 48 9 #define I40E_MAX_PROFILE_NUM 16 10 #define I40E_PROFILE_LIST_SIZE \ 11 (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4) 12 #define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/" 13 #define I40E_DDP_PROFILE_NAME_MAX 64 14 15 struct i40e_ddp_profile_list { 16 u32 p_count; 17 struct i40e_profile_info p_info[]; 18 }; 19 20 struct i40e_ddp_old_profile_list { 21 struct list_head list; 22 size_t old_ddp_size; 23 u8 old_ddp_buf[]; 24 }; 25 26 /** 27 * i40e_ddp_profiles_eq - checks if DDP profiles are the equivalent 28 * @a: new profile info 29 * @b: old profile info 30 * 31 * checks if DDP profiles are the equivalent. 32 * Returns true if profiles are the same. 33 **/ 34 static bool i40e_ddp_profiles_eq(struct i40e_profile_info *a, 35 struct i40e_profile_info *b) 36 { 37 return a->track_id == b->track_id && 38 !memcmp(&a->version, &b->version, sizeof(a->version)) && 39 !memcmp(&a->name, &b->name, I40E_DDP_NAME_SIZE); 40 } 41 42 /** 43 * i40e_ddp_does_profile_exist - checks if DDP profile loaded already 44 * @hw: HW data structure 45 * @pinfo: DDP profile information structure 46 * 47 * checks if DDP profile loaded already. 48 * Returns >0 if the profile exists. 49 * Returns 0 if the profile is absent. 50 * Returns <0 if error. 51 **/ 52 static int i40e_ddp_does_profile_exist(struct i40e_hw *hw, 53 struct i40e_profile_info *pinfo) 54 { 55 struct i40e_ddp_profile_list *profile_list; 56 u8 buff[I40E_PROFILE_LIST_SIZE]; 57 int status; 58 int i; 59 60 status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, 61 NULL); 62 if (status) 63 return -1; 64 65 profile_list = (struct i40e_ddp_profile_list *)buff; 66 for (i = 0; i < profile_list->p_count; i++) { 67 if (i40e_ddp_profiles_eq(pinfo, &profile_list->p_info[i])) 68 return 1; 69 } 70 return 0; 71 } 72 73 /** 74 * i40e_ddp_profiles_overlap - checks if DDP profiles overlap. 75 * @new: new profile info 76 * @old: old profile info 77 * 78 * checks if DDP profiles overlap. 79 * Returns true if profiles are overlap. 80 **/ 81 static bool i40e_ddp_profiles_overlap(struct i40e_profile_info *new, 82 struct i40e_profile_info *old) 83 { 84 unsigned int group_id_old = FIELD_GET(0x00FF0000, old->track_id); 85 unsigned int group_id_new = FIELD_GET(0x00FF0000, new->track_id); 86 87 /* 0x00 group must be only the first */ 88 if (group_id_new == 0) 89 return true; 90 /* 0xFF group is compatible with anything else */ 91 if (group_id_new == 0xFF || group_id_old == 0xFF) 92 return false; 93 /* otherwise only profiles from the same group are compatible*/ 94 return group_id_old != group_id_new; 95 } 96 97 /** 98 * i40e_ddp_does_profile_overlap - checks if DDP overlaps with existing one. 99 * @hw: HW data structure 100 * @pinfo: DDP profile information structure 101 * 102 * checks if DDP profile overlaps with existing one. 103 * Returns >0 if the profile overlaps. 104 * Returns 0 if the profile is ok. 105 * Returns <0 if error. 106 **/ 107 static int i40e_ddp_does_profile_overlap(struct i40e_hw *hw, 108 struct i40e_profile_info *pinfo) 109 { 110 struct i40e_ddp_profile_list *profile_list; 111 u8 buff[I40E_PROFILE_LIST_SIZE]; 112 int status; 113 int i; 114 115 status = i40e_aq_get_ddp_list(hw, buff, I40E_PROFILE_LIST_SIZE, 0, 116 NULL); 117 if (status) 118 return -EIO; 119 120 profile_list = (struct i40e_ddp_profile_list *)buff; 121 for (i = 0; i < profile_list->p_count; i++) { 122 if (i40e_ddp_profiles_overlap(pinfo, 123 &profile_list->p_info[i])) 124 return 1; 125 } 126 return 0; 127 } 128 129 /** 130 * i40e_add_pinfo 131 * @hw: pointer to the hardware structure 132 * @profile: pointer to the profile segment of the package 133 * @profile_info_sec: buffer for information section 134 * @track_id: package tracking id 135 * 136 * Register a profile to the list of loaded profiles. 137 */ 138 static int 139 i40e_add_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, 140 u8 *profile_info_sec, u32 track_id) 141 { 142 struct i40e_profile_section_header *sec; 143 struct i40e_profile_info *pinfo; 144 u32 offset = 0, info = 0; 145 int status; 146 147 sec = (struct i40e_profile_section_header *)profile_info_sec; 148 sec->tbl_size = 1; 149 sec->data_end = sizeof(struct i40e_profile_section_header) + 150 sizeof(struct i40e_profile_info); 151 sec->section.type = SECTION_TYPE_INFO; 152 sec->section.offset = sizeof(struct i40e_profile_section_header); 153 sec->section.size = sizeof(struct i40e_profile_info); 154 pinfo = (struct i40e_profile_info *)(profile_info_sec + 155 sec->section.offset); 156 pinfo->track_id = track_id; 157 pinfo->version = profile->version; 158 pinfo->op = I40E_DDP_ADD_TRACKID; 159 160 /* Clear reserved field */ 161 memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); 162 memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE); 163 164 status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, 165 track_id, &offset, &info, NULL); 166 return status; 167 } 168 169 /** 170 * i40e_del_pinfo - delete DDP profile info from NIC 171 * @hw: HW data structure 172 * @profile: DDP profile segment to be deleted 173 * @profile_info_sec: DDP profile section header 174 * @track_id: track ID of the profile for deletion 175 * 176 * Removes DDP profile from the NIC. 177 **/ 178 static int 179 i40e_del_pinfo(struct i40e_hw *hw, struct i40e_profile_segment *profile, 180 u8 *profile_info_sec, u32 track_id) 181 { 182 struct i40e_profile_section_header *sec; 183 struct i40e_profile_info *pinfo; 184 u32 offset = 0, info = 0; 185 int status; 186 187 sec = (struct i40e_profile_section_header *)profile_info_sec; 188 sec->tbl_size = 1; 189 sec->data_end = sizeof(struct i40e_profile_section_header) + 190 sizeof(struct i40e_profile_info); 191 sec->section.type = SECTION_TYPE_INFO; 192 sec->section.offset = sizeof(struct i40e_profile_section_header); 193 sec->section.size = sizeof(struct i40e_profile_info); 194 pinfo = (struct i40e_profile_info *)(profile_info_sec + 195 sec->section.offset); 196 pinfo->track_id = track_id; 197 pinfo->version = profile->version; 198 pinfo->op = I40E_DDP_REMOVE_TRACKID; 199 200 /* Clear reserved field */ 201 memset(pinfo->reserved, 0, sizeof(pinfo->reserved)); 202 memcpy(pinfo->name, profile->name, I40E_DDP_NAME_SIZE); 203 204 status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, 205 track_id, &offset, &info, NULL); 206 return status; 207 } 208 209 /** 210 * i40e_ddp_is_pkg_hdr_valid - performs basic pkg header integrity checks 211 * @netdev: net device structure (for logging purposes) 212 * @pkg_hdr: pointer to package header 213 * @size_huge: size of the whole DDP profile package in size_t 214 * 215 * Checks correctness of pkg header: Version, size too big/small, and 216 * all segment offsets alignment and boundaries. This function lets 217 * reject non DDP profile file to be loaded by administrator mistake. 218 **/ 219 static bool i40e_ddp_is_pkg_hdr_valid(struct net_device *netdev, 220 struct i40e_package_header *pkg_hdr, 221 size_t size_huge) 222 { 223 u32 size = 0xFFFFFFFFU & size_huge; 224 u32 pkg_hdr_size; 225 u32 segment; 226 227 if (!pkg_hdr) 228 return false; 229 230 if (pkg_hdr->version.major > 0) { 231 struct i40e_ddp_version ver = pkg_hdr->version; 232 233 netdev_err(netdev, "Unsupported DDP profile version %u.%u.%u.%u", 234 ver.major, ver.minor, ver.update, ver.draft); 235 return false; 236 } 237 if (size_huge > size) { 238 netdev_err(netdev, "Invalid DDP profile - size is bigger than 4G"); 239 return false; 240 } 241 if (size < (sizeof(struct i40e_package_header) + sizeof(u32) + 242 sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { 243 netdev_err(netdev, "Invalid DDP profile - size is too small."); 244 return false; 245 } 246 247 pkg_hdr_size = sizeof(u32) * (pkg_hdr->segment_count + 2U); 248 if (size < pkg_hdr_size) { 249 netdev_err(netdev, "Invalid DDP profile - too many segments"); 250 return false; 251 } 252 for (segment = 0; segment < pkg_hdr->segment_count; ++segment) { 253 u32 offset = pkg_hdr->segment_offset[segment]; 254 255 if (0xFU & offset) { 256 netdev_err(netdev, 257 "Invalid DDP profile %u segment alignment", 258 segment); 259 return false; 260 } 261 if (pkg_hdr_size > offset || offset >= size) { 262 netdev_err(netdev, 263 "Invalid DDP profile %u segment offset", 264 segment); 265 return false; 266 } 267 } 268 269 return true; 270 } 271 272 /** 273 * i40e_ddp_load - performs DDP loading 274 * @netdev: net device structure 275 * @data: buffer containing recipe file 276 * @size: size of the buffer 277 * @is_add: true when loading profile, false when rolling back the previous one 278 * 279 * Checks correctness and loads DDP profile to the NIC. The function is 280 * also used for rolling back previously loaded profile. 281 **/ 282 static int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size, 283 bool is_add) 284 { 285 u8 profile_info_sec[sizeof(struct i40e_profile_section_header) + 286 sizeof(struct i40e_profile_info)]; 287 struct i40e_metadata_segment *metadata_hdr; 288 struct i40e_profile_segment *profile_hdr; 289 struct i40e_profile_info pinfo; 290 struct i40e_package_header *pkg_hdr; 291 struct i40e_netdev_priv *np = netdev_priv(netdev); 292 struct i40e_vsi *vsi = np->vsi; 293 struct i40e_pf *pf = vsi->back; 294 u32 track_id; 295 int istatus; 296 int status; 297 298 pkg_hdr = (struct i40e_package_header *)data; 299 if (!i40e_ddp_is_pkg_hdr_valid(netdev, pkg_hdr, size)) 300 return -EINVAL; 301 302 if (size < (sizeof(struct i40e_package_header) + sizeof(u32) + 303 sizeof(struct i40e_metadata_segment) + sizeof(u32) * 2)) { 304 netdev_err(netdev, "Invalid DDP recipe size."); 305 return -EINVAL; 306 } 307 308 /* Find beginning of segment data in buffer */ 309 metadata_hdr = (struct i40e_metadata_segment *) 310 i40e_find_segment_in_package(SEGMENT_TYPE_METADATA, pkg_hdr); 311 if (!metadata_hdr) { 312 netdev_err(netdev, "Failed to find metadata segment in DDP recipe."); 313 return -EINVAL; 314 } 315 316 track_id = metadata_hdr->track_id; 317 profile_hdr = (struct i40e_profile_segment *) 318 i40e_find_segment_in_package(SEGMENT_TYPE_I40E, pkg_hdr); 319 if (!profile_hdr) { 320 netdev_err(netdev, "Failed to find profile segment in DDP recipe."); 321 return -EINVAL; 322 } 323 324 pinfo.track_id = track_id; 325 pinfo.version = profile_hdr->version; 326 if (is_add) 327 pinfo.op = I40E_DDP_ADD_TRACKID; 328 else 329 pinfo.op = I40E_DDP_REMOVE_TRACKID; 330 331 memcpy(pinfo.name, profile_hdr->name, I40E_DDP_NAME_SIZE); 332 333 /* Check if profile data already exists*/ 334 istatus = i40e_ddp_does_profile_exist(&pf->hw, &pinfo); 335 if (istatus < 0) { 336 netdev_err(netdev, "Failed to fetch loaded profiles."); 337 return istatus; 338 } 339 if (is_add) { 340 if (istatus > 0) { 341 netdev_err(netdev, "DDP profile already loaded."); 342 return -EINVAL; 343 } 344 istatus = i40e_ddp_does_profile_overlap(&pf->hw, &pinfo); 345 if (istatus < 0) { 346 netdev_err(netdev, "Failed to fetch loaded profiles."); 347 return istatus; 348 } 349 if (istatus > 0) { 350 netdev_err(netdev, "DDP profile overlaps with existing one."); 351 return -EINVAL; 352 } 353 } else { 354 if (istatus == 0) { 355 netdev_err(netdev, 356 "DDP profile for deletion does not exist."); 357 return -EINVAL; 358 } 359 } 360 361 /* Load profile data */ 362 if (is_add) { 363 status = i40e_write_profile(&pf->hw, profile_hdr, track_id); 364 if (status) { 365 if (status == -ENODEV) { 366 netdev_err(netdev, 367 "Profile is not supported by the device."); 368 return -EPERM; 369 } 370 netdev_err(netdev, "Failed to write DDP profile."); 371 return -EIO; 372 } 373 } else { 374 status = i40e_rollback_profile(&pf->hw, profile_hdr, track_id); 375 if (status) { 376 netdev_err(netdev, "Failed to remove DDP profile."); 377 return -EIO; 378 } 379 } 380 381 /* Add/remove profile to/from profile list in FW */ 382 if (is_add) { 383 status = i40e_add_pinfo(&pf->hw, profile_hdr, profile_info_sec, 384 track_id); 385 if (status) { 386 netdev_err(netdev, "Failed to add DDP profile info."); 387 return -EIO; 388 } 389 } else { 390 status = i40e_del_pinfo(&pf->hw, profile_hdr, profile_info_sec, 391 track_id); 392 if (status) { 393 netdev_err(netdev, "Failed to restore DDP profile info."); 394 return -EIO; 395 } 396 } 397 398 return 0; 399 } 400 401 /** 402 * i40e_ddp_restore - restore previously loaded profile and remove from list 403 * @pf: PF data struct 404 * 405 * Restores previously loaded profile stored on the list in driver memory. 406 * After rolling back removes entry from the list. 407 **/ 408 static int i40e_ddp_restore(struct i40e_pf *pf) 409 { 410 struct i40e_ddp_old_profile_list *entry; 411 struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev; 412 int status = 0; 413 414 if (!list_empty(&pf->ddp_old_prof)) { 415 entry = list_first_entry(&pf->ddp_old_prof, 416 struct i40e_ddp_old_profile_list, 417 list); 418 status = i40e_ddp_load(netdev, entry->old_ddp_buf, 419 entry->old_ddp_size, false); 420 list_del(&entry->list); 421 kfree(entry); 422 } 423 return status; 424 } 425 426 /** 427 * i40e_ddp_flash - callback function for ethtool flash feature 428 * @netdev: net device structure 429 * @flash: kernel flash structure 430 * 431 * Ethtool callback function used for loading and unloading DDP profiles. 432 **/ 433 int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash) 434 { 435 const struct firmware *ddp_config; 436 struct i40e_netdev_priv *np = netdev_priv(netdev); 437 struct i40e_vsi *vsi = np->vsi; 438 struct i40e_pf *pf = vsi->back; 439 int status = 0; 440 441 /* Check for valid region first */ 442 if (flash->region != I40_DDP_FLASH_REGION) { 443 netdev_err(netdev, "Requested firmware region is not recognized by this driver."); 444 return -EINVAL; 445 } 446 if (pf->hw.bus.func != 0) { 447 netdev_err(netdev, "Any DDP operation is allowed only on Phy0 NIC interface"); 448 return -EINVAL; 449 } 450 451 /* If the user supplied "-" instead of file name rollback previously 452 * stored profile. 453 */ 454 if (strncmp(flash->data, "-", 2) != 0) { 455 struct i40e_ddp_old_profile_list *list_entry; 456 char profile_name[sizeof(I40E_DDP_PROFILE_PATH) 457 + I40E_DDP_PROFILE_NAME_MAX]; 458 459 scnprintf(profile_name, sizeof(profile_name), "%s%s", 460 I40E_DDP_PROFILE_PATH, flash->data); 461 462 /* Load DDP recipe. */ 463 status = request_firmware(&ddp_config, profile_name, 464 &netdev->dev); 465 if (status) { 466 netdev_err(netdev, "DDP recipe file request failed."); 467 return status; 468 } 469 470 status = i40e_ddp_load(netdev, ddp_config->data, 471 ddp_config->size, true); 472 473 if (!status) { 474 list_entry = 475 kzalloc(sizeof(struct i40e_ddp_old_profile_list) + 476 ddp_config->size, GFP_KERNEL); 477 if (!list_entry) { 478 netdev_info(netdev, "Failed to allocate memory for previous DDP profile data."); 479 netdev_info(netdev, "New profile loaded but roll-back will be impossible."); 480 } else { 481 memcpy(list_entry->old_ddp_buf, 482 ddp_config->data, ddp_config->size); 483 list_entry->old_ddp_size = ddp_config->size; 484 list_add(&list_entry->list, &pf->ddp_old_prof); 485 } 486 } 487 488 release_firmware(ddp_config); 489 } else { 490 if (!list_empty(&pf->ddp_old_prof)) { 491 status = i40e_ddp_restore(pf); 492 } else { 493 netdev_warn(netdev, "There is no DDP profile to restore."); 494 status = -ENOENT; 495 } 496 } 497 return status; 498 } 499