Commit 13b89768 authored by Manivannan Sadhasivam's avatar Manivannan Sadhasivam Committed by Miquel Raynal
Browse files

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: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20210402150128.29128-4-manivannan.sadhasivam@linaro.org
parent ee590106
Loading
Loading
Loading
Loading
+99 −1
Original line number Original line Diff line number Diff line
@@ -278,11 +278,48 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
	return 0;
	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)
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)
	if (chip->options & NAND_NO_BBM_QUIRK)
		return 0;
		return 0;


	/* Check if the region is secured */
	if (nand_region_is_secured(chip, ofs, mtd->erasesize))
		return -EIO;

	if (chip->legacy.block_bad)
	if (chip->legacy.block_bad)
		return chip->legacy.block_bad(chip, ofs);
		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;
		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);
	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;
	int retry_mode = 0;
	bool ecc_fail = false;
	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);
	chipnr = (int)(from >> chip->chip_shift);
	nand_select_target(chip, chipnr);
	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",
	pr_debug("%s: from = 0x%08Lx, len = %i\n",
			__func__, (unsigned long long)from, readlen);
			__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;
	stats = mtd->ecc_stats;


	len = mtd_oobavail(mtd, ops);
	len = mtd_oobavail(mtd, ops);
@@ -3980,6 +4029,10 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
		return -EINVAL;
		return -EINVAL;
	}
	}


	/* Check if the region is secured */
	if (nand_region_is_secured(chip, to, writelen))
		return -EIO;

	column = to & (mtd->writesize - 1);
	column = to & (mtd->writesize - 1);


	chipnr = (int)(to >> chip->chip_shift);
	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))
	if (check_offs_len(chip, instr->addr, instr->len))
		return -EINVAL;
		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 */
	/* Grab the lock and see if the device is available */
	ret = nand_get_device(chip);
	ret = nand_get_device(chip);
	if (ret)
	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");
	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)
static int rawnand_dt_init(struct nand_chip *chip)
{
{
	struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(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;
			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 */
	/* Check, if we should skip the bad block table scan */
	if (chip->options & NAND_SKIP_BBTSCAN)
	if (chip->options & NAND_SKIP_BBTSCAN)
		return 0;
		return 0;
@@ -5959,10 +6051,13 @@ static int nand_scan_tail(struct nand_chip *chip)
	/* Build bad block table */
	/* Build bad block table */
	ret = nand_create_bbt(chip);
	ret = nand_create_bbt(chip);
	if (ret)
	if (ret)
		goto err_free_interface_config;
		goto err_free_secure_regions;


	return 0;
	return 0;


err_free_secure_regions:
	kfree(chip->secure_regions);

err_free_interface_config:
err_free_interface_config:
	kfree(chip->best_interface_config);
	kfree(chip->best_interface_config);


@@ -6050,6 +6145,9 @@ void nand_cleanup(struct nand_chip *chip)


	nanddev_cleanup(&chip->base);
	nanddev_cleanup(&chip->base);


	/* Free secure regions data */
	kfree(chip->secure_regions);

	/* Free bad block table memory */
	/* Free bad block table memory */
	kfree(chip->bbt);
	kfree(chip->bbt);
	kfree(chip->data_buf);
	kfree(chip->data_buf);
+14 −0
Original line number Original line Diff line number Diff line
@@ -1035,6 +1035,16 @@ struct nand_manufacturer {
	void *priv;
	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
 * struct nand_chip - NAND Private Flash Chip Data
 * @base: Inherit from the generic NAND device
 * @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
 *          NAND Controller drivers should not modify this value, but they're
 *          allowed to read it.
 *          allowed to read it.
 * @read_retries: The number of read retry modes supported
 * @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
 * @controller: The hardware controller	structure which is shared among multiple
 *              independent devices
 *              independent devices
 * @ecc: The ECC controller structure
 * @ecc: The ECC controller structure
@@ -1134,6 +1146,8 @@ struct nand_chip {
	unsigned int suspended : 1;
	unsigned int suspended : 1;
	int cur_cs;
	int cur_cs;
	int read_retries;
	int read_retries;
	struct nand_secure_region *secure_regions;
	u8 nr_secure_regions;


	/* Externals */
	/* Externals */
	struct nand_controller *controller;
	struct nand_controller *controller;