summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw/nand_base.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-04-27 02:16:09 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2021-04-27 02:16:09 +0300
commit070a7252d21b8e8900ee8540f82f0f1a348f8816 (patch)
tree4383b804e188d40f0004f96229a5f312b3b7860d /drivers/mtd/nand/raw/nand_base.c
parente19eede54240d64b4baf9b0df4dfb8191f7ae48b (diff)
parenta881537dfaf281bfcb94313d69dcf9ef8fc89afe (diff)
downloadlinux-070a7252d21b8e8900ee8540f82f0f1a348f8816.tar.xz
Merge tag 'mtd/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull mtd updates from Miquel Raynal: "MTD core: - Handle possible -EPROBE_DEFER from parse_mtd_partitions() - Constify buf in mtd_write_user_prot_reg() - Constify name param in mtd_bdi_init - Fix fall-through warnings for Clang - Get rid of Big MTD Lock ouf of mtdchar - Drop mtd_mutex usage from mtdchar_open() - Don't lock when recursively deleting partitions - Use module_mtd_blktrans() to register driver when relevant - Parse MTD as NVMEM cells - New OTP (one-time-programmable) erase ioctl - Require write permissions for locking and badblock ioctls - physmap: - Fix error return code of physmap_flash_remove() - physmap-bt1-rom: Fix unintentional stack access - ofpart parser: - Support Linksys Northstar partitions - Make symbol 'bcm4908_partitions_quirks' static - Limit parsing of deprecated DT syntax - Support BCM4908 fixed partitions - Qcom parser: - Incompatible with spi-nor 4k sectors - Fix error condition - Extend Qcom SMEM parser to SPI flash CFI: - Disable broken buffered writes for CFI chips within ID 0x2201 - Address a Coverity report for unused value SPI NOR core: - Add OTP support - Fix module unload while an op in progress - Add various cleanup patches - Add Michael and Pratyush as designated reviewers in MAINTAINERS SPI NOR controller drivers: - intel-spi: - Move platform data header to x86 subfolder NAND core: - Fix error handling in nand_prog_page_op() (x2) - Add a helper to retrieve the number of ECC bytes per step - Add a helper to retrieve the number of ECC steps - Let ECC engines advertize the exact number of steps - ECC Hamming: - Populate the public nsteps field - Use the public nsteps field - ECC BCH: - Populate the public nsteps field - Use the public nsteps field Raw NAND core: - Add support for secure regions in NAND memory - Try not to use the ECC private structures - Remove duplicate include in rawnand.h - BBT: - Skip bad blocks when searching for the BBT in NAND (APPLIED THEN REVERTED) Raw NAND controller drivers: - Qcom: - Convert bindings to YAML - Use dma_mapping_error() for error check - Add missing nand_cleanup() in error path - Return actual error code instead of -ENODEV - Update last code word register - Add helper to configure location register - Rename parameter name in macro - Add helper to check last code word - Convert nandc to chip in Read/Write helper - Update register macro name for 0x2c offset - GPMI: - Fix a double free in gpmi_nand_init - Rockchip: - Use flexible-array member instead of zero-length array - Atmel: - Update ecc_stats.corrected counter - MXC: - Remove unneeded of_match_ptr() - R852: - replace spin_lock_irqsave by spin_lock in hard IRQ - Brcmnand: - Move to polling in pio mode on oops write - Read/write oob during EDU transfer - Fix OOB R/W with Hamming ECC - FSMC: - Fix error code in fsmc_nand_probe() - OMAP: - Use ECC information from the generic structures SPI-NAND core: - Add missing MODULE_DEVICE_TABLE() SPI-NAND drivers: - gigadevice: Support GD5F1GQ5UExxG" * tag 'mtd/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (89 commits) Revert "mtd: rawnand: bbt: Skip bad blocks when searching for the BBT in NAND" mtd: core: Constify buf in mtd_write_user_prot_reg() Revert "mtd: spi-nor: macronix: Add support for mx25l51245g" mtd: spi-nor: core: Fix an issue of releasing resources during read/write mtd: cfi_cmdset_0002: remove redundant assignment to variable timeo mtd: cfi_cmdset_0002: Disable buffered writes for AMD chip 0x2201 mtd: rawnand: qcom: Use dma_mapping_error() for error check mtd: rawnand: gpmi: Fix a double free in gpmi_nand_init mtd: rawnand: qcom: Add missing nand_cleanup() in error path mtd: rawnand: Add support for secure regions in NAND memory dt-bindings: mtd: Add a property to declare secure regions in NAND chips dt-bindings: mtd: Convert Qcom NANDc binding to YAML mtd: spi-nor: winbond: add OTP support to w25q32fw/jw mtd: spi-nor: implement OTP support for Winbond and similar flashes mtd: spi-nor: add OTP support mtd: spi-nor: swp: Improve code around spi_nor_check_lock_status_sr() mtd: spi-nor: Move Software Write Protection logic out of the core mtd: rawnand: bbt: Skip bad blocks when searching for the BBT in NAND include: linux: mtd: Remove duplicate include of nand.h mtd: parsers: ofpart: support Linksys Northstar partitions ...
Diffstat (limited to 'drivers/mtd/nand/raw/nand_base.c')
-rw-r--r--drivers/mtd/nand/raw/nand_base.c145
1 files changed, 121 insertions, 24 deletions
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index c33fa1b1847f..fb072c444495 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -278,11 +278,48 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
return 0;
}
+/**
+ * nand_region_is_secured() - Check if the region is secured
+ * @chip: NAND chip object
+ * @offset: Offset of the region to check
+ * @size: Size of the region to check
+ *
+ * Checks if the region is secured by comparing the offset and size with the
+ * list of secure regions obtained from DT. Returns true if the region is
+ * secured else false.
+ */
+static bool nand_region_is_secured(struct nand_chip *chip, loff_t offset, u64 size)
+{
+ int i;
+
+ /* Skip touching the secure regions if present */
+ for (i = 0; i < chip->nr_secure_regions; i++) {
+ const struct nand_secure_region *region = &chip->secure_regions[i];
+
+ if (offset + size <= region->offset ||
+ offset >= region->offset + region->size)
+ continue;
+
+ pr_debug("%s: Region 0x%llx - 0x%llx is secured!",
+ __func__, offset, offset + size);
+
+ return true;
+ }
+
+ return false;
+}
+
static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
if (chip->options & NAND_NO_BBM_QUIRK)
return 0;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, ofs, mtd->erasesize))
+ return -EIO;
+
if (chip->legacy.block_bad)
return chip->legacy.block_bad(chip, ofs);
@@ -397,6 +434,10 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
return -EINVAL;
}
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, to, ops->ooblen))
+ return -EIO;
+
chipnr = (int)(to >> chip->chip_shift);
/*
@@ -1294,8 +1335,6 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
- int ret;
- u8 status;
if (naddrs < 0)
return naddrs;
@@ -1335,15 +1374,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
op.ninstrs--;
}
- ret = nand_exec_op(chip, &op);
- if (!prog || ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
-
- return status;
+ return nand_exec_op(chip, &op);
}
/**
@@ -1449,7 +1480,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
unsigned int len)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int status;
+ u8 status;
+ int ret;
if (!len || !buf)
return -EINVAL;
@@ -1458,14 +1490,24 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
return -EINVAL;
if (nand_has_exec_op(chip)) {
- status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
len, true);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
} else {
chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page,
page);
chip->legacy.write_buf(chip, buf, len);
chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
}
if (status & NAND_STATUS_FAIL)
@@ -3127,6 +3169,10 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
int retry_mode = 0;
bool ecc_fail = false;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, from, readlen))
+ return -EIO;
+
chipnr = (int)(from >> chip->chip_shift);
nand_select_target(chip, chipnr);
@@ -3458,6 +3504,10 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
pr_debug("%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, from, readlen))
+ return -EIO;
+
stats = mtd->ecc_stats;
len = mtd_oobavail(mtd, ops);
@@ -3979,6 +4029,10 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
return -EINVAL;
}
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, to, writelen))
+ return -EIO;
+
column = to & (mtd->writesize - 1);
chipnr = (int)(to >> chip->chip_shift);
@@ -4180,6 +4234,10 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
if (check_offs_len(chip, instr->addr, instr->len))
return -EINVAL;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, instr->addr, instr->len))
+ return -EIO;
+
/* Grab the lock and see if the device is available */
ret = nand_get_device(chip);
if (ret)
@@ -4995,6 +5053,31 @@ static bool of_get_nand_on_flash_bbt(struct device_node *np)
return of_property_read_bool(np, "nand-on-flash-bbt");
}
+static int of_get_nand_secure_regions(struct nand_chip *chip)
+{
+ struct device_node *dn = nand_get_flash_node(chip);
+ int nr_elem, i, j;
+
+ nr_elem = of_property_count_elems_of_size(dn, "secure-regions", sizeof(u64));
+ if (!nr_elem)
+ return 0;
+
+ chip->nr_secure_regions = nr_elem / 2;
+ chip->secure_regions = kcalloc(chip->nr_secure_regions, sizeof(*chip->secure_regions),
+ GFP_KERNEL);
+ if (!chip->secure_regions)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < chip->nr_secure_regions; i++, j += 2) {
+ of_property_read_u64_index(dn, "secure-regions", j,
+ &chip->secure_regions[i].offset);
+ of_property_read_u64_index(dn, "secure-regions", j + 1,
+ &chip->secure_regions[i].size);
+ }
+
+ return 0;
+}
+
static int rawnand_dt_init(struct nand_chip *chip)
{
struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
@@ -5162,8 +5245,8 @@ int rawnand_sw_hamming_init(struct nand_chip *chip)
chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength;
chip->ecc.total = base->ecc.ctx.total;
- chip->ecc.steps = engine_conf->nsteps;
- chip->ecc.bytes = engine_conf->code_size;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
return 0;
}
@@ -5201,7 +5284,7 @@ EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
int rawnand_sw_bch_init(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
- struct nand_ecc_sw_bch_conf *engine_conf;
+ const struct nand_ecc_props *ecc_conf = nanddev_get_ecc_conf(base);
int ret;
base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
@@ -5213,13 +5296,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
if (ret)
return ret;
- engine_conf = base->ecc.ctx.priv;
-
- chip->ecc.size = base->ecc.ctx.conf.step_size;
- chip->ecc.strength = base->ecc.ctx.conf.strength;
+ chip->ecc.size = ecc_conf->step_size;
+ chip->ecc.strength = ecc_conf->strength;
chip->ecc.total = base->ecc.ctx.total;
- chip->ecc.steps = engine_conf->nsteps;
- chip->ecc.bytes = engine_conf->code_size;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
return 0;
}
@@ -5953,6 +6034,16 @@ static int nand_scan_tail(struct nand_chip *chip)
goto err_free_interface_config;
}
+ /*
+ * Look for secure regions in the NAND chip. These regions are supposed
+ * to be protected by a secure element like Trustzone. So the read/write
+ * accesses to these regions will be blocked in the runtime by this
+ * driver.
+ */
+ ret = of_get_nand_secure_regions(chip);
+ if (ret)
+ goto err_free_interface_config;
+
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
@@ -5960,10 +6051,13 @@ static int nand_scan_tail(struct nand_chip *chip)
/* Build bad block table */
ret = nand_create_bbt(chip);
if (ret)
- goto err_free_interface_config;
+ goto err_free_secure_regions;
return 0;
+err_free_secure_regions:
+ kfree(chip->secure_regions);
+
err_free_interface_config:
kfree(chip->best_interface_config);
@@ -6051,6 +6145,9 @@ void nand_cleanup(struct nand_chip *chip)
nanddev_cleanup(&chip->base);
+ /* Free secure regions data */
+ kfree(chip->secure_regions);
+
/* Free bad block table memory */
kfree(chip->bbt);
kfree(chip->data_buf);