diff options
Diffstat (limited to 'drivers/net/ipa/ipa_cmd.c')
| -rw-r--r-- | drivers/net/ipa/ipa_cmd.c | 78 | 
1 files changed, 49 insertions, 29 deletions
| diff --git a/drivers/net/ipa/ipa_cmd.c b/drivers/net/ipa/ipa_cmd.c index 35e35852c25c..525cdf28d9ea 100644 --- a/drivers/net/ipa/ipa_cmd.c +++ b/drivers/net/ipa/ipa_cmd.c @@ -71,13 +71,12 @@ struct ipa_cmd_hw_hdr_init_local {  /* IPA_CMD_REGISTER_WRITE */ -/* For IPA v4.0+, this opcode gets modified with pipeline clear options */ - +/* For IPA v4.0+, the pipeline clear options are encoded in the opcode */  #define REGISTER_WRITE_OPCODE_SKIP_CLEAR_FMASK		GENMASK(8, 8)  #define REGISTER_WRITE_OPCODE_CLEAR_OPTION_FMASK	GENMASK(10, 9)  struct ipa_cmd_register_write { -	__le16 flags;		/* Unused/reserved for IPA v3.5.1 */ +	__le16 flags;		/* Unused/reserved prior to IPA v4.0 */  	__le16 offset;  	__le32 value;  	__le32 value_mask; @@ -85,12 +84,12 @@ struct ipa_cmd_register_write {  };  /* Field masks for ipa_cmd_register_write structure fields */ -/* The next field is present for IPA v4.0 and above */ +/* The next field is present for IPA v4.0+ */  #define REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK		GENMASK(14, 11) -/* The next field is present for IPA v3.5.1 only */ +/* The next field is not present for IPA v4.0+ */  #define REGISTER_WRITE_FLAGS_SKIP_CLEAR_FMASK		GENMASK(15, 15) -/* The next field and its values are present for IPA v3.5.1 only */ +/* The next field and its values are not present for IPA v4.0+ */  #define REGISTER_WRITE_CLEAR_OPTIONS_FMASK		GENMASK(1, 0)  /* IPA_CMD_IP_PACKET_INIT */ @@ -123,7 +122,7 @@ struct ipa_cmd_hw_dma_mem_mem {  /* Field masks for ipa_cmd_hw_dma_mem_mem structure fields */  #define DMA_SHARED_MEM_FLAGS_DIRECTION_FMASK		GENMASK(0, 0) -/* The next two fields are present for IPA v3.5.1 only. */ +/* The next two fields are not present for IPA v4.0+ */  #define DMA_SHARED_MEM_FLAGS_SKIP_CLEAR_FMASK		GENMASK(1, 1)  #define DMA_SHARED_MEM_FLAGS_CLEAR_OPTIONS_FMASK	GENMASK(3, 2) @@ -154,7 +153,7 @@ static void ipa_cmd_validate_build(void)  	 * of entries, as and IPv4 and IPv6 route tables have the same number  	 * of entries.  	 */ -#define TABLE_SIZE	(TABLE_COUNT_MAX * IPA_TABLE_ENTRY_SIZE) +#define TABLE_SIZE	(TABLE_COUNT_MAX * sizeof(__le64))  #define TABLE_COUNT_MAX	max_t(u32, IPA_ROUTE_COUNT_MAX, IPA_FILTER_COUNT_MAX)  	BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_HASH_SIZE_FMASK));  	BUILD_BUG_ON(TABLE_SIZE > field_max(IP_FLTRT_FLAGS_NHASH_SIZE_FMASK)); @@ -175,21 +174,23 @@ bool ipa_cmd_table_valid(struct ipa *ipa, const struct ipa_mem *mem,  			    : field_max(IP_FLTRT_FLAGS_NHASH_ADDR_FMASK);  	if (mem->offset > offset_max ||  	    ipa->mem_offset > offset_max - mem->offset) { -		dev_err(dev, "IPv%c %s%s table region offset too large " -			      "(0x%04x + 0x%04x > 0x%04x)\n", -			      ipv6 ? '6' : '4', hashed ? "hashed " : "", -			      route ? "route" : "filter", -			      ipa->mem_offset, mem->offset, offset_max); +		dev_err(dev, "IPv%c %s%s table region offset too large\n", +			ipv6 ? '6' : '4', hashed ? "hashed " : "", +			route ? "route" : "filter"); +		dev_err(dev, "    (0x%04x + 0x%04x > 0x%04x)\n", +			ipa->mem_offset, mem->offset, offset_max); +  		return false;  	}  	if (mem->offset > ipa->mem_size ||  	    mem->size > ipa->mem_size - mem->offset) { -		dev_err(dev, "IPv%c %s%s table region out of range " -			      "(0x%04x + 0x%04x > 0x%04x)\n", -			      ipv6 ? '6' : '4', hashed ? "hashed " : "", -			      route ? "route" : "filter", -			      mem->offset, mem->size, ipa->mem_size); +		dev_err(dev, "IPv%c %s%s table region out of range\n", +			ipv6 ? '6' : '4', hashed ? "hashed " : "", +			route ? "route" : "filter"); +		dev_err(dev, "    (0x%04x + 0x%04x > 0x%04x)\n", +			mem->offset, mem->size, ipa->mem_size); +  		return false;  	} @@ -205,22 +206,36 @@ static bool ipa_cmd_header_valid(struct ipa *ipa)  	u32 size_max;  	u32 size; +	/* In ipa_cmd_hdr_init_local_add() we record the offset and size +	 * of the header table memory area.  Make sure the offset and size +	 * fit in the fields that need to hold them, and that the entire +	 * range is within the overall IPA memory range. +	 */  	offset_max = field_max(HDR_INIT_LOCAL_FLAGS_HDR_ADDR_FMASK);  	if (mem->offset > offset_max ||  	    ipa->mem_offset > offset_max - mem->offset) { -		dev_err(dev, "header table region offset too large " -			      "(0x%04x + 0x%04x > 0x%04x)\n", -			      ipa->mem_offset + mem->offset, offset_max); +		dev_err(dev, "header table region offset too large\n"); +		dev_err(dev, "    (0x%04x + 0x%04x > 0x%04x)\n", +			ipa->mem_offset, mem->offset, offset_max); +  		return false;  	}  	size_max = field_max(HDR_INIT_LOCAL_FLAGS_TABLE_SIZE_FMASK);  	size = ipa->mem[IPA_MEM_MODEM_HEADER].size;  	size += ipa->mem[IPA_MEM_AP_HEADER].size; -	if (mem->offset > ipa->mem_size || size > ipa->mem_size - mem->offset) { -		dev_err(dev, "header table region out of range " -			      "(0x%04x + 0x%04x > 0x%04x)\n", -			      mem->offset, size, ipa->mem_size); + +	if (size > size_max) { +		dev_err(dev, "header table region size too large\n"); +		dev_err(dev, "    (0x%04x > 0x%08x)\n", size, size_max); + +		return false; +	} +	if (size > ipa->mem_size || mem->offset > ipa->mem_size - size) { +		dev_err(dev, "header table region out of range\n"); +		dev_err(dev, "    (0x%04x + 0x%04x > 0x%04x)\n", +			mem->offset, size, ipa->mem_size); +  		return false;  	} @@ -237,11 +252,12 @@ static bool ipa_cmd_register_write_offset_valid(struct ipa *ipa,  	u32 bit_count;  	/* The maximum offset in a register_write immediate command depends -	 * on the version of IPA.  IPA v3.5.1 supports a 16 bit offset, but -	 * newer versions allow some additional high-order bits. +	 * on the version of IPA.  A 16 bit offset is always supported, +	 * but starting with IPA v4.0 some additional high-order bits are +	 * allowed.  	 */  	bit_count = BITS_PER_BYTE * sizeof(payload->offset); -	if (ipa->version != IPA_VERSION_3_5_1) +	if (ipa->version >= IPA_VERSION_4_0)  		bit_count += hweight32(REGISTER_WRITE_FLAGS_OFFSET_HIGH_FMASK);  	BUILD_BUG_ON(bit_count > 32);  	offset_max = ~0U >> (32 - bit_count); @@ -440,7 +456,11 @@ void ipa_cmd_register_write_add(struct gsi_trans *trans, u32 offset, u32 value,  	/* pipeline_clear_src_grp is not used */  	clear_option = clear_full ? pipeline_clear_full : pipeline_clear_hps; -	if (ipa->version != IPA_VERSION_3_5_1) { +	/* IPA v4.0+ represents the pipeline clear options in the opcode.  It +	 * also supports a larger offset by encoding additional high-order +	 * bits in the payload flags field. +	 */ +	if (ipa->version >= IPA_VERSION_4_0) {  		u16 offset_high;  		u32 val; | 
