Skip to content

Commit

Permalink
nvmem: eeprom: at25: fix FRAM byte_len
Browse files Browse the repository at this point in the history
Commit fd307a4 ("nvmem: prepare basics for FRAM support") added
support for FRAM devices such as the Cypress FM25V. During testing, it
was found that the FRAM detects properly, however reads and writes fail.
Upon further investigation, two problem were found in at25_probe() routine.

1) In the case of an FRAM device without platform data, eg.
       fram == true && spi->dev.platform_data == NULL
the stack local variable "struct spi_eeprom chip" is not initialized
fully, prior to being copied into at25->chip. The chip.flags field in
particular can cause problems.

2) The byte_len of FRAM is computed from its ID register, and is stored
into the stack local "struct spi_eeprom chip" structure. This happens
after the same structure has been copied into at25->chip. As a result,
at25->chip.byte_len does not contain the correct length of the device.
In turn this can cause checks at beginning of at25_ee_read() to fail
(or equally, it could allow reads beyond the end of the device length).

Fix both of these issues by eliminating the on-stack struct spi_eeprom.
Instead use the one inside at25_data structure, which starts of zeroed.

Fixes: fd307a4 ("nvmem: prepare basics for FRAM support")
Cc: stable <stable@vger.kernel.org>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
Link: https://lore.kernel.org/r/20211108181627.645638-1-ralph.siemsen@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Ralph Siemsen authored and Greg Kroah-Hartman committed Dec 3, 2021
1 parent 3a1bf59 commit 9a62657
Showing 1 changed file with 18 additions and 20 deletions.
38 changes: 18 additions & 20 deletions drivers/misc/eeprom/at25.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,6 @@ MODULE_DEVICE_TABLE(spi, at25_spi_ids);
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
struct spi_eeprom chip;
int err;
int sr;
u8 id[FM25_ID_LEN];
Expand All @@ -389,15 +388,18 @@ static int at25_probe(struct spi_device *spi)
if (match && !strcmp(match->compatible, "cypress,fm25"))
is_fram = 1;

at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL);
if (!at25)
return -ENOMEM;

/* Chip description */
if (!spi->dev.platform_data) {
if (!is_fram) {
err = at25_fw_to_chip(&spi->dev, &chip);
if (err)
return err;
}
} else
chip = *(struct spi_eeprom *)spi->dev.platform_data;
if (spi->dev.platform_data) {
memcpy(&at25->chip, spi->dev.platform_data, sizeof(at25->chip));
} else if (!is_fram) {
err = at25_fw_to_chip(&spi->dev, &at25->chip);
if (err)
return err;
}

/* Ping the chip ... the status register is pretty portable,
* unlike probing manufacturer IDs. We do expect that system
Expand All @@ -409,12 +411,7 @@ static int at25_probe(struct spi_device *spi)
return -ENXIO;
}

at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL);
if (!at25)
return -ENOMEM;

mutex_init(&at25->lock);
at25->chip = chip;
at25->spi = spi;
spi_set_drvdata(spi, at25);

Expand All @@ -431,7 +428,7 @@ static int at25_probe(struct spi_device *spi)
dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]);
return -ENODEV;
}
chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024;
at25->chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024;

if (at25->chip.byte_len > 64 * 1024)
at25->chip.flags |= EE_ADDR3;
Expand Down Expand Up @@ -464,7 +461,7 @@ static int at25_probe(struct spi_device *spi)
at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM;
at25->nvmem_config.name = dev_name(&spi->dev);
at25->nvmem_config.dev = &spi->dev;
at25->nvmem_config.read_only = chip.flags & EE_READONLY;
at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY;
at25->nvmem_config.root_only = true;
at25->nvmem_config.owner = THIS_MODULE;
at25->nvmem_config.compat = true;
Expand All @@ -474,17 +471,18 @@ static int at25_probe(struct spi_device *spi)
at25->nvmem_config.priv = at25;
at25->nvmem_config.stride = 1;
at25->nvmem_config.word_size = 1;
at25->nvmem_config.size = chip.byte_len;
at25->nvmem_config.size = at25->chip.byte_len;

at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config);
if (IS_ERR(at25->nvmem))
return PTR_ERR(at25->nvmem);

dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n",
(chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
(chip.byte_len < 1024) ? "Byte" : "KByte",
(at25->chip.byte_len < 1024) ?
at25->chip.byte_len : (at25->chip.byte_len / 1024),
(at25->chip.byte_len < 1024) ? "Byte" : "KByte",
at25->chip.name, is_fram ? "fram" : "eeprom",
(chip.flags & EE_READONLY) ? " (readonly)" : "",
(at25->chip.flags & EE_READONLY) ? " (readonly)" : "",
at25->chip.page_size);
return 0;
}
Expand Down

0 comments on commit 9a62657

Please sign in to comment.