[PATCH v3] mtd: cfi_cmdset_0001: Byte swap OTP info

Nicolas Pitre nico at fluxnic.net
Fri Jun 2 14:11:24 PDT 2023


On Fri, 2 Jun 2023, Linus Walleij wrote:

> Currently the offset into the device when looking for OTP
> bits can go outside of the address of the MTD NOR devices,
> and if that memory isn't readable, bad things happen
> on the IXP4xx (added prints that illustrate the problem before
> the crash):
> 
> cfi_intelext_otp_walk walk OTP on chip 0 start at reg_prot_offset 0x00000100
> ixp4xx_copy_from copy from 0x00000100 to 0xc880dd78
> cfi_intelext_otp_walk walk OTP on chip 0 start at reg_prot_offset 0x12000000
> ixp4xx_copy_from copy from 0x12000000 to 0xc880dd78
> 8<--- cut here ---
> Unable to handle kernel paging request at virtual address db000000
> [db000000] *pgd=00000000
> (...)
> 
> This happens in this case because the IXP4xx is big endian and
> the 32- and 16-bit fields in the struct cfi_intelext_otpinfo are not
> properly byteswapped. Compare to how the code in read_pri_intelext()
> byteswaps the fields in struct cfi_pri_intelext.
> 
> Adding a small byte swapping loop for the OTP in read_pri_intelext()
> and the crash goes away.
> 
> The problem went unnoticed for many years until I enabled
> CONFIG_MTD_OTP on the IXP4xx as well, triggering the bug.
> 
> Cc: Nicolas Pitre <npitre at baylibre.com>
> Cc: stable at vger.kernel.org
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>

Reviewed-by: Nicolas Pitre <nico at fluxnic.net>

> ---
> ChangeLog v2->v3:
> - Move the byte swapping to a small loop in read_pri_intelext()
>   so all bytes are swapped as we reach cfi_intelext_otp_walk().
> ChangeLog v1->v2:
> - Drill deeper and discover a big endian compatibility issue.
> ---
>  drivers/mtd/chips/cfi_cmdset_0001.c | 20 ++++++++++++++++++--
>  1 file changed, 18 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
> index 54f92d09d9cf..02aaf09d6f5c 100644
> --- a/drivers/mtd/chips/cfi_cmdset_0001.c
> +++ b/drivers/mtd/chips/cfi_cmdset_0001.c
> @@ -421,9 +421,25 @@ read_pri_intelext(struct map_info *map, __u16 adr)
>  		extra_size = 0;
>  
>  		/* Protection Register info */
> -		if (extp->NumProtectionFields)
> +		if (extp->NumProtectionFields) {
> +			struct cfi_intelext_otpinfo *otp =
> +				(struct cfi_intelext_otpinfo *)&extp->extra[0];
> +
>  			extra_size += (extp->NumProtectionFields - 1) *
> -				      sizeof(struct cfi_intelext_otpinfo);
> +				sizeof(struct cfi_intelext_otpinfo);
> +
> +			if (extp_size >= sizeof(*extp) + extra_size) {
> +				int i;
> +
> +				/* Do some byteswapping if necessary */
> +				for (i = 0; i < extp->NumProtectionFields - 1; i++) {
> +					otp->ProtRegAddr = le32_to_cpu(otp->ProtRegAddr);
> +					otp->FactGroups = le16_to_cpu(otp->FactGroups);
> +					otp->UserGroups = le16_to_cpu(otp->UserGroups);
> +					otp++;
> +				}
> +			}
> +		}
>  	}
>  
>  	if (extp->MinorVersion >= '1') {
> -- 
> 2.40.1
> 
> 



More information about the linux-mtd mailing list