mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
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:
parent
ee590106c3
commit
13b8976827
2 changed files with 113 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue