Skip to content

Commit

Permalink
mtd: rawnand: Add support for secure regions in NAND memory
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Manivannan Sadhasivam authored and Miquel Raynal committed Apr 7, 2021
1 parent ee59010 commit 13b8976
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 1 deletion.
100 changes: 99 additions & 1 deletion drivers/mtd/nand/raw/nand_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

/*
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -5952,17 +6034,30 @@ 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;

/* 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);

Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions include/linux/mtd/rawnand.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 13b8976

Please sign in to comment.