cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

onenand_bbt.c (6626B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  Bad Block Table support for the OneNAND driver
      4 *
      5 *  Copyright(c) 2005 Samsung Electronics
      6 *  Kyungmin Park <kyungmin.park@samsung.com>
      7 *
      8 *  Derived from nand_bbt.c
      9 *
     10 *  TODO:
     11 *    Split BBT core and chip specific BBT.
     12 */
     13
     14#include <linux/slab.h>
     15#include <linux/mtd/mtd.h>
     16#include <linux/mtd/onenand.h>
     17#include <linux/export.h>
     18
     19/**
     20 * check_short_pattern - [GENERIC] check if a pattern is in the buffer
     21 * @buf:		the buffer to search
     22 * @len:		the length of buffer to search
     23 * @paglen:	the pagelength
     24 * @td:		search pattern descriptor
     25 *
     26 * Check for a pattern at the given place. Used to search bad block
     27 * tables and good / bad block identifiers. Same as check_pattern, but
     28 * no optional empty check and the pattern is expected to start
     29 * at offset 0.
     30 *
     31 */
     32static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
     33{
     34	int i;
     35	uint8_t *p = buf;
     36
     37	/* Compare the pattern */
     38	for (i = 0; i < td->len; i++) {
     39		if (p[i] != td->pattern[i])
     40			return -1;
     41	}
     42        return 0;
     43}
     44
     45/**
     46 * create_bbt - [GENERIC] Create a bad block table by scanning the device
     47 * @mtd:		MTD device structure
     48 * @buf:		temporary buffer
     49 * @bd:		descriptor for the good/bad block search pattern
     50 * @chip:		create the table for a specific chip, -1 read all chips.
     51 *              Applies only if NAND_BBT_PERCHIP option is set
     52 *
     53 * Create a bad block table by scanning the device
     54 * for the given good/bad block identify pattern
     55 */
     56static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
     57{
     58	struct onenand_chip *this = mtd->priv;
     59	struct bbm_info *bbm = this->bbm;
     60	int i, j, numblocks, len, scanlen;
     61	int startblock;
     62	loff_t from;
     63	size_t readlen;
     64	struct mtd_oob_ops ops;
     65	int rgn;
     66
     67	printk(KERN_INFO "Scanning device for bad blocks\n");
     68
     69	len = 2;
     70
     71	/* We need only read few bytes from the OOB area */
     72	scanlen = 0;
     73	readlen = bd->len;
     74
     75	/* chip == -1 case only */
     76	/* Note that numblocks is 2 * (real numblocks) here;
     77	 * see i += 2 below as it makses shifting and masking less painful
     78	 */
     79	numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
     80	startblock = 0;
     81	from = 0;
     82
     83	ops.mode = MTD_OPS_PLACE_OOB;
     84	ops.ooblen = readlen;
     85	ops.oobbuf = buf;
     86	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
     87
     88	for (i = startblock; i < numblocks; ) {
     89		int ret;
     90
     91		for (j = 0; j < len; j++) {
     92			/* No need to read pages fully,
     93			 * just read required OOB bytes */
     94			ret = onenand_bbt_read_oob(mtd,
     95				from + j * this->writesize + bd->offs, &ops);
     96
     97			/* If it is a initial bad block, just ignore it */
     98			if (ret == ONENAND_BBT_READ_FATAL_ERROR)
     99				return -EIO;
    100
    101			if (ret || check_short_pattern(&buf[j * scanlen],
    102					       scanlen, this->writesize, bd)) {
    103				bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
    104				printk(KERN_INFO "OneNAND eraseblock %d is an "
    105					"initial bad block\n", i >> 1);
    106				mtd->ecc_stats.badblocks++;
    107				break;
    108			}
    109		}
    110		i += 2;
    111
    112		if (FLEXONENAND(this)) {
    113			rgn = flexonenand_region(mtd, from);
    114			from += mtd->eraseregions[rgn].erasesize;
    115		} else
    116			from += (1 << bbm->bbt_erase_shift);
    117	}
    118
    119	return 0;
    120}
    121
    122
    123/**
    124 * onenand_memory_bbt - [GENERIC] create a memory based bad block table
    125 * @mtd:		MTD device structure
    126 * @bd:		descriptor for the good/bad block search pattern
    127 *
    128 * The function creates a memory based bbt by scanning the device
    129 * for manufacturer / software marked good / bad blocks
    130 */
    131static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
    132{
    133	struct onenand_chip *this = mtd->priv;
    134
    135	return create_bbt(mtd, this->page_buf, bd, -1);
    136}
    137
    138/**
    139 * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
    140 * @mtd:		MTD device structure
    141 * @offs:		offset in the device
    142 * @allowbbt:	allow access to bad block table region
    143 */
    144static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
    145{
    146	struct onenand_chip *this = mtd->priv;
    147	struct bbm_info *bbm = this->bbm;
    148	int block;
    149	uint8_t res;
    150
    151	/* Get block number * 2 */
    152	block = (int) (onenand_block(this, offs) << 1);
    153	res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
    154
    155	pr_debug("onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
    156		(unsigned int) offs, block >> 1, res);
    157
    158	switch ((int) res) {
    159	case 0x00:	return 0;
    160	case 0x01:	return 1;
    161	case 0x02:	return allowbbt ? 0 : 1;
    162	}
    163
    164	return 1;
    165}
    166
    167/**
    168 * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
    169 * @mtd:		MTD device structure
    170 * @bd:		descriptor for the good/bad block search pattern
    171 *
    172 * The function checks, if a bad block table(s) is/are already
    173 * available. If not it scans the device for manufacturer
    174 * marked good / bad blocks and writes the bad block table(s) to
    175 * the selected place.
    176 *
    177 * The bad block table memory is allocated here. It is freed
    178 * by the onenand_release function.
    179 *
    180 */
    181static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
    182{
    183	struct onenand_chip *this = mtd->priv;
    184	struct bbm_info *bbm = this->bbm;
    185	int len, ret = 0;
    186
    187	len = this->chipsize >> (this->erase_shift + 2);
    188	/* Allocate memory (2bit per block) and clear the memory bad block table */
    189	bbm->bbt = kzalloc(len, GFP_KERNEL);
    190	if (!bbm->bbt)
    191		return -ENOMEM;
    192
    193	/* Set erase shift */
    194	bbm->bbt_erase_shift = this->erase_shift;
    195
    196	if (!bbm->isbad_bbt)
    197		bbm->isbad_bbt = onenand_isbad_bbt;
    198
    199	/* Scan the device to build a memory based bad block table */
    200	if ((ret = onenand_memory_bbt(mtd, bd))) {
    201		printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
    202		kfree(bbm->bbt);
    203		bbm->bbt = NULL;
    204	}
    205
    206	return ret;
    207}
    208
    209/*
    210 * Define some generic bad / good block scan pattern which are used
    211 * while scanning a device for factory marked good / bad blocks.
    212 */
    213static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
    214
    215static struct nand_bbt_descr largepage_memorybased = {
    216	.options = 0,
    217	.offs = 0,
    218	.len = 2,
    219	.pattern = scan_ff_pattern,
    220};
    221
    222/**
    223 * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
    224 * @mtd:		MTD device structure
    225 *
    226 * This function selects the default bad block table
    227 * support for the device and calls the onenand_scan_bbt function
    228 */
    229int onenand_default_bbt(struct mtd_info *mtd)
    230{
    231	struct onenand_chip *this = mtd->priv;
    232	struct bbm_info *bbm;
    233
    234	this->bbm = kzalloc(sizeof(struct bbm_info), GFP_KERNEL);
    235	if (!this->bbm)
    236		return -ENOMEM;
    237
    238	bbm = this->bbm;
    239
    240	/* 1KB page has same configuration as 2KB page */
    241	if (!bbm->badblock_pattern)
    242		bbm->badblock_pattern = &largepage_memorybased;
    243
    244	return onenand_scan_bbt(mtd, bbm->badblock_pattern);
    245}