^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2006-2008 Nokia Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Test page read and write on MTD device.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <asm/div64.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/mtd/mtd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/random.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include "mtd_test.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static int dev = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) module_param(dev, int, S_IRUGO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) MODULE_PARM_DESC(dev, "MTD device number to use");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static struct mtd_info *mtd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static unsigned char *twopages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static unsigned char *writebuf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) static unsigned char *boundary;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) static unsigned char *bbt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static int pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static int bufsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static int ebcnt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int pgcnt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static int errcnt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static struct rnd_state rnd_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static int write_eraseblock(int ebnum)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) loff_t addr = (loff_t)ebnum * mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) cond_resched();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) static int verify_eraseblock(int ebnum)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) uint32_t j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) int err = 0, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) loff_t addr0, addrn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) loff_t addr = (loff_t)ebnum * mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) addr0 = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) for (i = 0; i < ebcnt && bbt[i]; ++i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) addr0 += mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) addrn = mtd->size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) addrn -= mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* Do a read to set the internal dataRAMs to different data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) err = mtdtest_read(mtd, addr0, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) memset(twopages, 0, bufsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) err = mtdtest_read(mtd, addr, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) pr_err("error: verify failed at %#llx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) (long long)addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) /* Check boundary between eraseblocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) struct rnd_state old_state = rnd_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) /* Do a read to set the internal dataRAMs to different data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) err = mtdtest_read(mtd, addr0, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) memset(twopages, 0, bufsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) err = mtdtest_read(mtd, addr, bufsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (memcmp(twopages, boundary, bufsize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) pr_err("error: verify failed at %#llx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) (long long)addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) rnd_state = old_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static int crosstest(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) int err = 0, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) loff_t addr, addr0, addrn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) unsigned char *pp1, *pp2, *pp3, *pp4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) pr_info("crosstest\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) pp1 = kcalloc(pgsize, 4, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (!pp1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) pp2 = pp1 + pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) pp3 = pp2 + pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) pp4 = pp3 + pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) addr0 = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) for (i = 0; i < ebcnt && bbt[i]; ++i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) addr0 += mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) addrn = mtd->size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) addrn -= mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /* Read 2nd-to-last page to pp1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) addr = addrn - pgsize - pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) err = mtdtest_read(mtd, addr, pgsize, pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) /* Read 3rd-to-last page to pp1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) addr = addrn - pgsize - pgsize - pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) err = mtdtest_read(mtd, addr, pgsize, pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) /* Read first page to pp2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) addr = addr0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) pr_info("reading page at %#llx\n", (long long)addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) err = mtdtest_read(mtd, addr, pgsize, pp2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) /* Read last page to pp3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) addr = addrn - pgsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) pr_info("reading page at %#llx\n", (long long)addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) err = mtdtest_read(mtd, addr, pgsize, pp3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) /* Read first page again to pp4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) addr = addr0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) pr_info("reading page at %#llx\n", (long long)addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) err = mtdtest_read(mtd, addr, pgsize, pp4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (err) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) /* pp2 and pp4 should be the same */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) pr_info("verifying pages read at %#llx match\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) (long long)addr0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if (memcmp(pp2, pp4, pgsize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) pr_err("verify failed!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) } else if (!err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) pr_info("crosstest ok\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) kfree(pp1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) static int erasecrosstest(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) int err = 0, i, ebnum, ebnum2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) loff_t addr0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) char *readbuf = twopages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) pr_info("erasecrosstest\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) ebnum = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) addr0 = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) for (i = 0; i < ebcnt && bbt[i]; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) addr0 += mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) ebnum += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) ebnum2 = ebcnt - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) while (ebnum2 && bbt[ebnum2])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) ebnum2 -= 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) pr_info("erasing block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) err = mtdtest_erase_eraseblock(mtd, ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) pr_info("writing 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) prandom_bytes_state(&rnd_state, writebuf, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) strcpy(writebuf, "There is no data like this!");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) err = mtdtest_write(mtd, addr0, pgsize, writebuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) pr_info("reading 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) memset(readbuf, 0, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) err = mtdtest_read(mtd, addr0, pgsize, readbuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) pr_info("verifying 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) if (memcmp(writebuf, readbuf, pgsize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) pr_err("verify failed!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) pr_info("erasing block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) err = mtdtest_erase_eraseblock(mtd, ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) pr_info("writing 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) prandom_bytes_state(&rnd_state, writebuf, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) strcpy(writebuf, "There is no data like this!");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) err = mtdtest_write(mtd, addr0, pgsize, writebuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) pr_info("erasing block %d\n", ebnum2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) err = mtdtest_erase_eraseblock(mtd, ebnum2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) pr_info("reading 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) memset(readbuf, 0, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) err = mtdtest_read(mtd, addr0, pgsize, readbuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) pr_info("verifying 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) if (memcmp(writebuf, readbuf, pgsize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) pr_err("verify failed!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) if (!err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) pr_info("erasecrosstest ok\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) static int erasetest(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) int err = 0, i, ebnum, ok = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) loff_t addr0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) pr_info("erasetest\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) ebnum = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) addr0 = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) for (i = 0; i < ebcnt && bbt[i]; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) addr0 += mtd->erasesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) ebnum += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) pr_info("erasing block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) err = mtdtest_erase_eraseblock(mtd, ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) pr_info("writing 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) prandom_bytes_state(&rnd_state, writebuf, pgsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) err = mtdtest_write(mtd, addr0, pgsize, writebuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) pr_info("erasing block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) err = mtdtest_erase_eraseblock(mtd, ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) pr_info("reading 1st page of block %d\n", ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) err = mtdtest_read(mtd, addr0, pgsize, twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) pr_info("verifying 1st page of block %d is all 0xff\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) ebnum);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) for (i = 0; i < pgsize; ++i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) if (twopages[i] != 0xff) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) pr_err("verifying all 0xff failed at %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) errcnt += 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) ok = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (ok && !err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) pr_info("erasetest ok\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) static int __init mtd_pagetest_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) uint64_t tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) uint32_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) printk(KERN_INFO "\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) printk(KERN_INFO "=================================================\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) if (dev < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) pr_info("Please specify a valid mtd-device via module parameter\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) pr_info("MTD device: %d\n", dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) mtd = get_mtd_device(NULL, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) if (IS_ERR(mtd)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) err = PTR_ERR(mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) pr_err("error: cannot get MTD device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) if (!mtd_type_is_nand(mtd)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) pr_info("this test requires NAND flash\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) tmp = mtd->size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) do_div(tmp, mtd->erasesize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) ebcnt = tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) pgcnt = mtd->erasesize / mtd->writesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) pgsize = mtd->writesize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) pr_info("MTD device size %llu, eraseblock size %u, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) "page size %u, count of eraseblocks %u, pages per "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) "eraseblock %u, OOB size %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) (unsigned long long)mtd->size, mtd->erasesize,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) pgsize, ebcnt, pgcnt, mtd->oobsize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) err = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) bufsize = pgsize * 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) if (!writebuf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) twopages = kmalloc(bufsize, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) if (!twopages)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) boundary = kmalloc(bufsize, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) if (!boundary)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) bbt = kzalloc(ebcnt, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) if (!bbt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) /* Erase all eraseblocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) pr_info("erasing whole device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) pr_info("erased %u eraseblocks\n", ebcnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) /* Write all eraseblocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) prandom_seed_state(&rnd_state, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) pr_info("writing whole device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) for (i = 0; i < ebcnt; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) if (bbt[i])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) err = write_eraseblock(i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) if (i % 256 == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) pr_info("written up to eraseblock %u\n", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) err = mtdtest_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) pr_info("written %u eraseblocks\n", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) /* Check all eraseblocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) prandom_seed_state(&rnd_state, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) pr_info("verifying all eraseblocks\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) for (i = 0; i < ebcnt; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if (bbt[i])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) err = verify_eraseblock(i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) if (i % 256 == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) pr_info("verified up to eraseblock %u\n", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) err = mtdtest_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) pr_info("verified %u eraseblocks\n", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) err = crosstest();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) if (ebcnt > 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) err = erasecrosstest();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) pr_info("skipping erasecrosstest, 2 erase blocks needed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) err = erasetest();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) pr_info("finished with %d errors\n", errcnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) kfree(bbt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) kfree(boundary);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) kfree(twopages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) kfree(writebuf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) put_mtd_device(mtd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) pr_info("error %d occurred\n", err);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) printk(KERN_INFO "=================================================\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) module_init(mtd_pagetest_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) static void __exit mtd_pagetest_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) module_exit(mtd_pagetest_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) MODULE_DESCRIPTION("NAND page test");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) MODULE_AUTHOR("Adrian Hunter");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) MODULE_LICENSE("GPL");