diff options
Diffstat (limited to 'drivers/mtd/nand/raw/nand_base.c')
| -rw-r--r-- | drivers/mtd/nand/raw/nand_base.c | 145 | 
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); | 
