aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/intel/i40e
diff options
context:
space:
mode:
authorAleksandr Loktionov <aleksandr.loktionov@intel.com>2019-02-06 18:08:16 -0500
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2019-04-16 18:10:21 -0400
commitcdc594e00370e153c323cf8aa9c43b66679e09a0 (patch)
tree0aec23f6666a5ec1b2d0fb5253a39bb17ca48bd2 /drivers/net/ethernet/intel/i40e
parent3e957b377bf4262aec2dd424f28ece94e36814d4 (diff)
i40e: Implement DDP support in i40e driver
This patch introduces DDP (Dynamic Device Personalization) which allows loading profiles that change the way internal parser interprets processed frames. To load DDP profiles it utilizes ethtool flash feature. The files with recipes must be located in /var/lib/firmware directory. Afterwards the recipe can be loaded by invoking: ethtool -f <if_name> <file_name> 100 ethtool -f <if_name> - 100 See further details of this feature in the i40e documentation, or visit https://www.intel.com/content/www/us/en/architecture-and-technology/ethernet/dynamic-device-personalization-brief.html The driver shall verify DDP profile can be loaded in accordance with the rules: * Package with Group ID 0 are exclusive and can only be loaded the first. * Packages with Group ID 0x01-0xFE can only be loaded simultaneously with the packages from the same group. * Packages with Group ID 0xFF are compatible with all other packages. Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com> Tested-by: Andrew Bowers <andrewx.bowers@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/i40e')
-rw-r--r--drivers/net/ethernet/intel/i40e/Makefile1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h25
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c253
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ddp.c481
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_prototype.h6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_type.h23
8 files changed, 769 insertions, 22 deletions
diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile
index 50590e8d1fd1..2f21b3e89fd0 100644
--- a/drivers/net/ethernet/intel/i40e/Makefile
+++ b/drivers/net/ethernet/intel/i40e/Makefile
@@ -21,6 +21,7 @@ i40e-objs := i40e_main.o \
21 i40e_diag.o \ 21 i40e_diag.o \
22 i40e_txrx.o \ 22 i40e_txrx.o \
23 i40e_ptp.o \ 23 i40e_ptp.o \
24 i40e_ddp.o \
24 i40e_client.o \ 25 i40e_client.o \
25 i40e_virtchnl_pf.o \ 26 i40e_virtchnl_pf.o \
26 i40e_xsk.o 27 i40e_xsk.o
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index d3cc3427caad..fc4cae2fef4f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -321,6 +321,29 @@ struct i40e_udp_port_config {
321 u8 filter_index; 321 u8 filter_index;
322}; 322};
323 323
324#define I40_DDP_FLASH_REGION 100
325#define I40E_PROFILE_INFO_SIZE 48
326#define I40E_MAX_PROFILE_NUM 16
327#define I40E_PROFILE_LIST_SIZE \
328 (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4)
329#define I40E_DDP_PROFILE_PATH "intel/i40e/ddp/"
330#define I40E_DDP_PROFILE_NAME_MAX 64
331
332int i40e_ddp_load(struct net_device *netdev, const u8 *data, size_t size,
333 bool is_add);
334int i40e_ddp_flash(struct net_device *netdev, struct ethtool_flash *flash);
335
336struct i40e_ddp_profile_list {
337 u32 p_count;
338 struct i40e_profile_info p_info[0];
339};
340
341struct i40e_ddp_old_profile_list {
342 struct list_head list;
343 size_t old_ddp_size;
344 u8 old_ddp_buf[0];
345};
346
324/* macros related to FLX_PIT */ 347/* macros related to FLX_PIT */
325#define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \ 348#define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \
326 I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \ 349 I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \
@@ -610,6 +633,8 @@ struct i40e_pf {
610 u16 override_q_count; 633 u16 override_q_count;
611 u16 last_sw_conf_flags; 634 u16 last_sw_conf_flags;
612 u16 last_sw_conf_valid_flags; 635 u16 last_sw_conf_valid_flags;
636 /* List to keep previous DDP profiles to be rolled back in the future */
637 struct list_head ddp_old_prof;
613}; 638};
614 639
615/** 640/**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 97a9b1fb4763..486a406789b8 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -5448,6 +5448,163 @@ i40e_find_segment_in_package(u32 segment_type,
5448 return NULL; 5448 return NULL;
5449} 5449}
5450 5450
5451/* Get section table in profile */
5452#define I40E_SECTION_TABLE(profile, sec_tbl) \
5453 do { \
5454 struct i40e_profile_segment *p = (profile); \
5455 u32 count; \
5456 u32 *nvm; \
5457 count = p->device_table_count; \
5458 nvm = (u32 *)&p->device_table[count]; \
5459 sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \
5460 } while (0)
5461
5462/* Get section header in profile */
5463#define I40E_SECTION_HEADER(profile, offset) \
5464 (struct i40e_profile_section_header *)((u8 *)(profile) + (offset))
5465
5466/**
5467 * i40e_find_section_in_profile
5468 * @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE)
5469 * @profile: pointer to the i40e segment header to be searched
5470 *
5471 * This function searches i40e segment for a particular section type. On
5472 * success it returns a pointer to the section header, otherwise it will
5473 * return NULL.
5474 **/
5475struct i40e_profile_section_header *
5476i40e_find_section_in_profile(u32 section_type,
5477 struct i40e_profile_segment *profile)
5478{
5479 struct i40e_profile_section_header *sec;
5480 struct i40e_section_table *sec_tbl;
5481 u32 sec_off;
5482 u32 i;
5483
5484 if (profile->header.type != SEGMENT_TYPE_I40E)
5485 return NULL;
5486
5487 I40E_SECTION_TABLE(profile, sec_tbl);
5488
5489 for (i = 0; i < sec_tbl->section_count; i++) {
5490 sec_off = sec_tbl->section_offset[i];
5491 sec = I40E_SECTION_HEADER(profile, sec_off);
5492 if (sec->section.type == section_type)
5493 return sec;
5494 }
5495
5496 return NULL;
5497}
5498
5499/**
5500 * i40e_ddp_exec_aq_section - Execute generic AQ for DDP
5501 * @hw: pointer to the hw struct
5502 * @aq: command buffer containing all data to execute AQ
5503 **/
5504static enum
5505i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw,
5506 struct i40e_profile_aq_section *aq)
5507{
5508 i40e_status status;
5509 struct i40e_aq_desc desc;
5510 u8 *msg = NULL;
5511 u16 msglen;
5512
5513 i40e_fill_default_direct_cmd_desc(&desc, aq->opcode);
5514 desc.flags |= cpu_to_le16(aq->flags);
5515 memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw));
5516
5517 msglen = aq->datalen;
5518 if (msglen) {
5519 desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF |
5520 I40E_AQ_FLAG_RD));
5521 if (msglen > I40E_AQ_LARGE_BUF)
5522 desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
5523 desc.datalen = cpu_to_le16(msglen);
5524 msg = &aq->data[0];
5525 }
5526
5527 status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL);
5528
5529 if (status) {
5530 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5531 "unable to exec DDP AQ opcode %u, error %d\n",
5532 aq->opcode, status);
5533 return status;
5534 }
5535
5536 /* copy returned desc to aq_buf */
5537 memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw));
5538
5539 return 0;
5540}
5541
5542/**
5543 * i40e_validate_profile
5544 * @hw: pointer to the hardware structure
5545 * @profile: pointer to the profile segment of the package to be validated
5546 * @track_id: package tracking id
5547 * @rollback: flag if the profile is for rollback.
5548 *
5549 * Validates supported devices and profile's sections.
5550 */
5551static enum i40e_status_code
5552i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
5553 u32 track_id, bool rollback)
5554{
5555 struct i40e_profile_section_header *sec = NULL;
5556 i40e_status status = 0;
5557 struct i40e_section_table *sec_tbl;
5558 u32 vendor_dev_id;
5559 u32 dev_cnt;
5560 u32 sec_off;
5561 u32 i;
5562
5563 if (track_id == I40E_DDP_TRACKID_INVALID) {
5564 i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n");
5565 return I40E_NOT_SUPPORTED;
5566 }
5567
5568 dev_cnt = profile->device_table_count;
5569 for (i = 0; i < dev_cnt; i++) {
5570 vendor_dev_id = profile->device_table[i].vendor_dev_id;
5571 if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL &&
5572 hw->device_id == (vendor_dev_id & 0xFFFF))
5573 break;
5574 }
5575 if (dev_cnt && i == dev_cnt) {
5576 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5577 "Device doesn't support DDP\n");
5578 return I40E_ERR_DEVICE_NOT_SUPPORTED;
5579 }
5580
5581 I40E_SECTION_TABLE(profile, sec_tbl);
5582
5583 /* Validate sections types */
5584 for (i = 0; i < sec_tbl->section_count; i++) {
5585 sec_off = sec_tbl->section_offset[i];
5586 sec = I40E_SECTION_HEADER(profile, sec_off);
5587 if (rollback) {
5588 if (sec->section.type == SECTION_TYPE_MMIO ||
5589 sec->section.type == SECTION_TYPE_AQ ||
5590 sec->section.type == SECTION_TYPE_RB_AQ) {
5591 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5592 "Not a roll-back package\n");
5593 return I40E_NOT_SUPPORTED;
5594 }
5595 } else {
5596 if (sec->section.type == SECTION_TYPE_RB_AQ ||
5597 sec->section.type == SECTION_TYPE_RB_MMIO) {
5598 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5599 "Not an original package\n");
5600 return I40E_NOT_SUPPORTED;
5601 }
5602 }
5603 }
5604
5605 return status;
5606}
5607
5451/** 5608/**
5452 * i40e_write_profile 5609 * i40e_write_profile
5453 * @hw: pointer to the hardware structure 5610 * @hw: pointer to the hardware structure
@@ -5463,47 +5620,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
5463 i40e_status status = 0; 5620 i40e_status status = 0;
5464 struct i40e_section_table *sec_tbl; 5621 struct i40e_section_table *sec_tbl;
5465 struct i40e_profile_section_header *sec = NULL; 5622 struct i40e_profile_section_header *sec = NULL;
5466 u32 dev_cnt; 5623 struct i40e_profile_aq_section *ddp_aq;
5467 u32 vendor_dev_id;
5468 u32 *nvm;
5469 u32 section_size = 0; 5624 u32 section_size = 0;
5470 u32 offset = 0, info = 0; 5625 u32 offset = 0, info = 0;
5626 u32 sec_off;
5471 u32 i; 5627 u32 i;
5472 5628
5473 dev_cnt = profile->device_table_count; 5629 status = i40e_validate_profile(hw, profile, track_id, false);
5630 if (status)
5631 return status;
5474 5632
5475 for (i = 0; i < dev_cnt; i++) { 5633 I40E_SECTION_TABLE(profile, sec_tbl);
5476 vendor_dev_id = profile->device_table[i].vendor_dev_id; 5634
5477 if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) 5635 for (i = 0; i < sec_tbl->section_count; i++) {
5478 if (hw->device_id == (vendor_dev_id & 0xFFFF)) 5636 sec_off = sec_tbl->section_offset[i];
5637 sec = I40E_SECTION_HEADER(profile, sec_off);
5638 /* Process generic admin command */
5639 if (sec->section.type == SECTION_TYPE_AQ) {
5640 ddp_aq = (struct i40e_profile_aq_section *)&sec[1];
5641 status = i40e_ddp_exec_aq_section(hw, ddp_aq);
5642 if (status) {
5643 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5644 "Failed to execute aq: section %d, opcode %u\n",
5645 i, ddp_aq->opcode);
5479 break; 5646 break;
5647 }
5648 sec->section.type = SECTION_TYPE_RB_AQ;
5649 }
5650
5651 /* Skip any non-mmio sections */
5652 if (sec->section.type != SECTION_TYPE_MMIO)
5653 continue;
5654
5655 section_size = sec->section.size +
5656 sizeof(struct i40e_profile_section_header);
5657
5658 /* Write MMIO section */
5659 status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
5660 track_id, &offset, &info, NULL);
5661 if (status) {
5662 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5663 "Failed to write profile: section %d, offset %d, info %d\n",
5664 i, offset, info);
5665 break;
5666 }
5480 } 5667 }
5481 if (i == dev_cnt) { 5668 return status;
5482 i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP"); 5669}
5483 return I40E_ERR_DEVICE_NOT_SUPPORTED; 5670
5484 } 5671/**
5672 * i40e_rollback_profile
5673 * @hw: pointer to the hardware structure
5674 * @profile: pointer to the profile segment of the package to be removed
5675 * @track_id: package tracking id
5676 *
5677 * Rolls back previously loaded package.
5678 */
5679enum i40e_status_code
5680i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
5681 u32 track_id)
5682{
5683 struct i40e_profile_section_header *sec = NULL;
5684 i40e_status status = 0;
5685 struct i40e_section_table *sec_tbl;
5686 u32 offset = 0, info = 0;
5687 u32 section_size = 0;
5688 u32 sec_off;
5689 int i;
5485 5690
5486 nvm = (u32 *)&profile->device_table[dev_cnt]; 5691 status = i40e_validate_profile(hw, profile, track_id, true);
5487 sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; 5692 if (status)
5693 return status;
5488 5694
5489 for (i = 0; i < sec_tbl->section_count; i++) { 5695 I40E_SECTION_TABLE(profile, sec_tbl);
5490 sec = (struct i40e_profile_section_header *)((u8 *)profile +
5491 sec_tbl->section_offset[i]);
5492 5696
5493 /* Skip 'AQ', 'note' and 'name' sections */ 5697 /* For rollback write sections in reverse */
5494 if (sec->section.type != SECTION_TYPE_MMIO) 5698 for (i = sec_tbl->section_count - 1; i >= 0; i--) {
5699 sec_off = sec_tbl->section_offset[i];
5700 sec = I40E_SECTION_HEADER(profile, sec_off);
5701
5702 /* Skip any non-rollback sections */
5703 if (sec->section.type != SECTION_TYPE_RB_MMIO)
5495 continue; 5704 continue;
5496 5705
5497 section_size = sec->section.size + 5706 section_size = sec->section.size +
5498 sizeof(struct i40e_profile_section_header); 5707 sizeof(struct i40e_profile_section_header);
5499 5708
5500 /* Write profile */ 5709 /* Write roll-back MMIO section */
5501 status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, 5710 status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
5502 track_id, &offset, &info, NULL); 5711 track_id, &offset, &info, NULL);
5503 if (status) { 5712 if (status) {
5504 i40e_debug(hw, I40E_DEBUG_PACKAGE, 5713 i40e_debug(hw, I40E_DEBUG_PACKAGE,
5505 "Failed to write profile: offset %d, info %d", 5714 "Failed to write profile: section %d, offset %d, info %d\n",
5506 offset, info); 5715 i, offset, info);
5507 break; 5716 break;
5508 } 5717 }
5509 } 5718 }
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ddp.c b/drivers/net/ethernet/intel/i40e/i40e_ddp.c
new file mode 100644
index 000000000000..5e08f100c413
--- /dev/null
+++ b/drivers/net/ethernet/intel/i40e/i40e_ddp.c
@@ -0,0 +1,481 @@
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 **/
16static 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 **/
34static 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 i40e_status 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 **/
63static 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_profiles_ - 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 **/
89static 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 i40e_status 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 */
120static enum i40e_status_code
121i40e_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 i40e_status status;
127 u32 offset = 0, info = 0;
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 **/
160static enum i40e_status_code
161i40e_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 i40e_status status;
167 u32 offset = 0, info = 0;
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 **/
201static 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) +
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 **/
264int 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 i40e_status status;
274 struct i40e_netdev_priv *np = netdev_priv(netdev);
275 struct i40e_vsi *vsi = np->vsi;
276 struct i40e_pf *pf = vsi->back;
277 u32 track_id;
278 int istatus;
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) +
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 == I40E_ERR_DEVICE_NOT_SUPPORTED) {
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 **/
390static 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 **/
415int 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}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 7874d0ec7fb0..1f9a3ee713f9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -5171,6 +5171,7 @@ static const struct ethtool_ops i40e_ethtool_ops = {
5171 .set_link_ksettings = i40e_set_link_ksettings, 5171 .set_link_ksettings = i40e_set_link_ksettings,
5172 .get_fecparam = i40e_get_fec_param, 5172 .get_fecparam = i40e_get_fec_param,
5173 .set_fecparam = i40e_set_fec_param, 5173 .set_fecparam = i40e_set_fec_param,
5174 .flash_device = i40e_ddp_flash,
5174}; 5175};
5175 5176
5176void i40e_set_ethtool_ops(struct net_device *netdev) 5177void i40e_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 1993b3708815..9de18aa8fa86 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -13987,6 +13987,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
13987 13987
13988 INIT_LIST_HEAD(&pf->l3_flex_pit_list); 13988 INIT_LIST_HEAD(&pf->l3_flex_pit_list);
13989 INIT_LIST_HEAD(&pf->l4_flex_pit_list); 13989 INIT_LIST_HEAD(&pf->l4_flex_pit_list);
13990 INIT_LIST_HEAD(&pf->ddp_old_prof);
13990 13991
13991 /* set up the locks for the AQ, do this only once in probe 13992 /* set up the locks for the AQ, do this only once in probe
13992 * and destroy them only once in remove 13993 * and destroy them only once in remove
diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
index e08d754824b1..663c8bf4d3d8 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
@@ -429,10 +429,16 @@ i40e_status i40e_aq_get_ddp_list(struct i40e_hw *hw, void *buff,
429struct i40e_generic_seg_header * 429struct i40e_generic_seg_header *
430i40e_find_segment_in_package(u32 segment_type, 430i40e_find_segment_in_package(u32 segment_type,
431 struct i40e_package_header *pkg_header); 431 struct i40e_package_header *pkg_header);
432struct i40e_profile_section_header *
433i40e_find_section_in_profile(u32 section_type,
434 struct i40e_profile_segment *profile);
432enum i40e_status_code 435enum i40e_status_code
433i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, 436i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg,
434 u32 track_id); 437 u32 track_id);
435enum i40e_status_code 438enum i40e_status_code
439i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg,
440 u32 track_id);
441enum i40e_status_code
436i40e_add_pinfo_to_list(struct i40e_hw *hw, 442i40e_add_pinfo_to_list(struct i40e_hw *hw,
437 struct i40e_profile_segment *profile, 443 struct i40e_profile_segment *profile,
438 u8 *profile_info_sec, u32 track_id); 444 u8 *profile_info_sec, u32 track_id);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
index 2781ab91ca82..79420bcc7414 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_type.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
@@ -1527,6 +1527,8 @@ struct i40e_generic_seg_header {
1527struct i40e_metadata_segment { 1527struct i40e_metadata_segment {
1528 struct i40e_generic_seg_header header; 1528 struct i40e_generic_seg_header header;
1529 struct i40e_ddp_version version; 1529 struct i40e_ddp_version version;
1530#define I40E_DDP_TRACKID_RDONLY 0
1531#define I40E_DDP_TRACKID_INVALID 0xFFFFFFFF
1530 u32 track_id; 1532 u32 track_id;
1531 char name[I40E_DDP_NAME_SIZE]; 1533 char name[I40E_DDP_NAME_SIZE];
1532}; 1534};
@@ -1555,15 +1557,36 @@ struct i40e_profile_section_header {
1555 struct { 1557 struct {
1556#define SECTION_TYPE_INFO 0x00000010 1558#define SECTION_TYPE_INFO 0x00000010
1557#define SECTION_TYPE_MMIO 0x00000800 1559#define SECTION_TYPE_MMIO 0x00000800
1560#define SECTION_TYPE_RB_MMIO 0x00001800
1558#define SECTION_TYPE_AQ 0x00000801 1561#define SECTION_TYPE_AQ 0x00000801
1562#define SECTION_TYPE_RB_AQ 0x00001801
1559#define SECTION_TYPE_NOTE 0x80000000 1563#define SECTION_TYPE_NOTE 0x80000000
1560#define SECTION_TYPE_NAME 0x80000001 1564#define SECTION_TYPE_NAME 0x80000001
1565#define SECTION_TYPE_PROTO 0x80000002
1566#define SECTION_TYPE_PCTYPE 0x80000003
1567#define SECTION_TYPE_PTYPE 0x80000004
1561 u32 type; 1568 u32 type;
1562 u32 offset; 1569 u32 offset;
1563 u32 size; 1570 u32 size;
1564 } section; 1571 } section;
1565}; 1572};
1566 1573
1574struct i40e_profile_tlv_section_record {
1575 u8 rtype;
1576 u8 type;
1577 u16 len;
1578 u8 data[12];
1579};
1580
1581/* Generic AQ section in proflie */
1582struct i40e_profile_aq_section {
1583 u16 opcode;
1584 u16 flags;
1585 u8 param[16];
1586 u16 datalen;
1587 u8 data[1];
1588};
1589
1567struct i40e_profile_info { 1590struct i40e_profile_info {
1568 u32 track_id; 1591 u32 track_id;
1569 struct i40e_ddp_version version; 1592 struct i40e_ddp_version version;