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