mtd: rawnand: Add support for secure regions in NAND memory

On a typical end product, a vendor may choose to secure some regions in
the NAND memory which are supposed to stay intact between FW upgrades.
The access to those regions will be blocked by a secure element like
Trustzone. So the normal world software like Linux kernel should not
touch these regions (including reading).

The regions are declared using a NAND chip DT property,
"secure-regions". So let's make use of this property in the raw NAND
core and skip access to the secure regions present in a system.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20210402150128.29128-4-manivannan.sadhasivam@linaro.org
This commit is contained in:
Manivannan Sadhasivam 2021-04-02 20:31:27 +05:30 committed by Miquel Raynal
parent ee590106c3
commit 13b8976827
2 changed files with 113 additions and 1 deletions

View file

@ -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);
/*
@ -3128,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);
@ -3459,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);
@ -3980,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);
@ -4181,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)
@ -4996,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));
@ -5952,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;
@ -5959,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);
@ -6050,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);

View file

@ -1035,6 +1035,16 @@ struct nand_manufacturer {
void *priv;
};
/**
* struct nand_secure_region - NAND secure region structure
* @offset: Offset of the start of the secure region
* @size: Size of the secure region
*/
struct nand_secure_region {
u64 offset;
u64 size;
};
/**
* struct nand_chip - NAND Private Flash Chip Data
* @base: Inherit from the generic NAND device
@ -1085,6 +1095,8 @@ struct nand_manufacturer {
* NAND Controller drivers should not modify this value, but they're
* allowed to read it.
* @read_retries: The number of read retry modes supported
* @secure_regions: Structure containing the secure regions info
* @nr_secure_regions: Number of secure regions
* @controller: The hardware controller structure which is shared among multiple
* independent devices
* @ecc: The ECC controller structure
@ -1134,6 +1146,8 @@ struct nand_chip {
unsigned int suspended : 1;
int cur_cs;
int read_retries;
struct nand_secure_region *secure_regions;
u8 nr_secure_regions;
/* Externals */
struct nand_controller *controller;