Mini2440之linux内核移植
在上一节我们已经介绍了u-boot启动linux内核的流程。这一节我们将对u-boot进行改造,使其支持linux-5.2.8版本内核启动。
linux kernel支持多种硬件,所谓内核移植概括的说,就是修改kernel中硬件相关的源码以适应自己的硬件。linux中硬件相关的代码主要集中在arch目录(体系结构相关、单板相关)以及drivers目录(设备驱动)。linux的移植对于产业链上下游的企业来说,要做的事情也不同,比如:
- IP核厂商:以ARM为例,ARM负责提供指令集以及支持该指令集的硬件实现——CPU核(公版)。ARM在移植kernel的时候,需要实现体系结构相关的代码,比如kernel启动的汇编阶段;
- SOC厂商:以ARM阵营为例,SOC厂商基于ARM的CPU核(当然也有自研的),添加一些片内外设形成自己的SOC。并且,一般还会基于自家的SOC做公版的开发板,从而方便推广自家产品。因此SOC厂商移植kernel的时候需要做的是,提供平台设备的驱动(即片内外设的驱动)、提供板级支持代码(arch/arm/mach-xxx)。
- 外设芯片厂商:这里的外设芯片指的是诸如ADC、各类传感器芯片等,比如TI的at24c0x芯片,一个完整的产品需要SOC+各路板载外设协同工作。外设芯片厂商为了推广自己的芯片(降低下游开发成本),需要为芯片提供各种环境的驱动程序,对于linux,他们要做的就是为linux提供其芯片产品的驱动。
- 电子产品厂商:下游应用厂商主要做方案整合(当然也有通吃全产业链的企业),即采购SOC+各种板载外设芯片,设计自己的电路板。他们要做的是,参考SOC厂商的公版开发板的板级支持代码,实现自己的板级支持代码。对于片内外设,根据SOC厂商的驱动来写相应的设备树;对于板载外设,根据外设芯片厂商的驱动来写设备树。所以底层这块,下游厂商其实发挥空间不大,底层支持主要还是看上游厂商,下游厂商的重点在于行业和应用。因此,网上有下游厂商的底软开发者调侃自己是“设备树工程师”。不过,即便是在下游厂商工作,熟悉kernel的原理也是比较重要的,毕竟你不能保证任何时候只用简单修改就能完成工作交付。
一、u-boot参数配置
我们将u-boot-2016.05-crop,复制一份命名为:u-boot-2016.05-linux。
1.1 源码修改
1.1.1 启动参数配置
在smdk2440.h(include/configs/smdk2440.h)文件中配置启动参数:
#define CONFIG_BOOTARGS "root=/dev/mtdblock3 console=ttySAC0,115200 init=/linuxrc"
- root:指定文件系统位置这里配置为NAND三号分区,也就是我们根文件系统所在的分区;
- init:指定内核启动后执行的第一个应用程序;
- console:指定使用哪个终端,这里的 ttySAC0 指的就是串口0;
1.1.2 支持yaffs2烧写
打开u-boot-2016.05-linux项目,进入nand的命令文件cmd/nand.c,在do_nand函数里,有nand read或write的代码,而其中有对jffs2的支持,却并没有对yaffs2的支持。
以前的老版本uboot是有对yaffs文件系统烧写的支持的,于是我们参考老版本的uboot代码,在do_nand函数里的nand write/read部分加上一段代码,如下:
#ifdef CONFIG_CMD_NAND_TRIMFFS } else if (!strcmp(s, ".trimffs")) { if (read) { printf("Unknown nand command suffix '%s'\n", s); return 1; } ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr, WITH_DROP_FFS | WITH_WR_VERIFY); #endif #ifdef CONFIG_CMD_NAND_YAFFS } else if (!strcmp(s, ".yaffs2")) { if (read) { printf("Unknown nand command suffix ‘%s‘.\n", s); return 1; } ret = nand_write_skip_bad(nand, off, &rwsize,NULL, //这里参数老版本 maxsize,(u_char *)addr, WITH_YAFFS_OOB); #endif
在nand_help_text[]里添加nand write.yaffs的帮助信息:
#ifdef CONFIG_CMD_NAND_TRIMFFS "nand write.trimffs - addr off|partition size\n" " write 'size' bytes starting at offset 'off' from memory address\n" " 'addr', skipping bad blocks and dropping any pages at the end\n" " of eraseblocks that contain only 0xFF\n" #endif #ifdef CONFIG_CMD_NAND_YAFFS "nand write.yaffs2 - addr off|partition size\n" " write 'size' bytes starting at offset 'off' with yaffs format\n" " from memory address 'addr', skipping bad blocks.\n" #endif
nand_write_skip_bad函数内部也要修改,该函数位于drivers/mtd/nand/nand_util.c文件:
if (actual) *actual = 0; #ifdef CONFIG_CMD_NAND_YAFFS if (flags & WITH_YAFFS_OOB) { if (flags & ~WITH_YAFFS_OOB) return -EINVAL; int pages; pages = nand->erasesize / nand->writesize; blocksize = (pages * nand->oobsize) + nand->erasesize; if (*length % (nand->writesize + nand->oobsize)) { printf ("Attempt to write incomplete page" " in yaffs mode\n"); return -EINVAL; } } else #endif { blocksize = nand->erasesize; } ... if (left_to_write < (blocksize - block_offset)) write_size = left_to_write; else write_size = blocksize - block_offset; #ifdef CONFIG_CMD_NAND_YAFFS if (flags & WITH_YAFFS_OOB) { int page, pages; size_t pagesize = nand->writesize; size_t pagesize_oob = pagesize + nand->oobsize; struct mtd_oob_ops ops; ops.len = pagesize; ops.ooblen = nand->oobsize; ops.mode = MTD_OPS_RAW; //这里要改为RAW ops.ooboffs = 0; pages = write_size / pagesize_oob; for (page = 0; page < pages; page++) { WATCHDOG_RESET(); ops.datbuf = p_buffer; ops.oobbuf = ops.datbuf + pagesize; rval = nand->_write_oob(nand, offset, &ops); if (rval != 0) break; offset += pagesize; p_buffer += pagesize_oob; } } else #endif { //这里要加个左大括号 truncated_write_size = write_size; #ifdef CONFIG_CMD_NAND_TRIMFFS if (flags & WITH_DROP_FFS) truncated_write_size = drop_ffs(nand, p_buffer, &write_size); #endif rval = nand_write(nand, offset, &truncated_write_size, p_buffer); if ((flags & WITH_WR_VERIFY) && !rval) rval = nand_verify(nand, offset, truncated_write_size, p_buffer); offset += write_size; p_buffer += write_size; } //这里要加个右大括号 if (rval != 0) {
同时,在include/nand.h中添加WITH_YAFFS_OOB宏的定义:
#define WITH_YAFFS_OOB (1 << 0) #define WITH_DROP_FFS (1 << 0)
最后在配置文件里include/configs/smdk2440.h添加CONFIG_CMD_NAND_YAFFS宏定义,编译烧写,此uboot已经支持yaffs2文件系统的烧写。
#define CONFIG_CMD_NAND_YAFFS /* 支持 nand write.yaffs2 - addr off|partition size 命令 */
1.1.3 启动命令配置
在smdk2440.h文件中配置启动命令:
#define CONFIG_BOOTCOMMAND "nand read 0x30000000 kernel; bootm 0x30000000" //bootcmd
1.1.4 设置matchid
linux内核在启动时,是通过u-boot传入的机器码确定应启动哪种目标平台的。
u-boot在不设置machid环境变量时,u-boot会使用默认的机器id,默认id在board_init函数中设置,该函数位于board/samsung/smdk2440/smdk2440.c:
int board_init(void) { /* arch number of SMDK2410-Board */ gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters */ gd->bd->bi_boot_params = 0x30000100; icache_enable(); dcache_enable(); return 0; }
我们搜索MACH_TYPE_SMDK2410:
root@zhengyang:/work/sambashare/u-boot-2016.05-linux# grep "MACH_TYPE_SMDK2410" * -nR
arch/arm/include/asm/mach-types.h:59:#define MACH_TYPE_SMDK2410 193
arch/arm/include/asm/mach-types.h:1644:# define machine_arch_type MACH_TYPE_SMDK2410
arch/arm/include/asm/mach-types.h:1646:# define machine_is_smdk2410() (machine_arch_type == MACH_TYPE_SMDK2410)
board/samsung/smdk2410/smdk2410.c:100: gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
board/samsung/smdk2440/smdk2440.c:100: gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
我们在arch/arm/include/asm/mach-types.h新增一行:
#define MACH_TYPE_SMDK2410 193 #define MACH_TYPE_SMDK2440 168 // 新增的
在文件后面新增:
#ifdef CONFIG_ARCH_SMDK2440
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
# else
# define machine_arch_type MACH_TYPE_SMDK2440
# endif
# define machine_is_smdk2440() (machine_arch_type == MACH_TYPE_SMDK2440)
#else
# define machine_is_smdk2440() (0)
#endif
并修改board_init函数:
/* arch number of SMDK2440-Board */ gd->bd->bi_arch_number = MACH_TYPE_SMDK2440;
1.2 编译下载
重新编译,下载u-boot到NAND FLASH:
make clean make distclean make smdk2440_defconfig make ARCH=arm CROSS_COMPILE=arm-linux- V=1
需要注意编译u-boot使用的是arm-linux-gcc4.3.2不要使用高版本,高版本编译出来的u-boot可能运行不了。
1.3 问题处理
等看了后面的内核编译,烧录内核到开发板,如果有问题再回过来看这里。
1.3.1 修改分区参数
在Mini440之uboot移植之裁剪、分区与环境变量设置(五)中我们曾经设置分区参数如下:
/* mtdparts command line support */ #define MTDIDS_DEFAULT "nand0=Mini2440-0" /* default mtd partition table */ #define MTDPARTS_DEFAULT "mtdparts=Mini2440-0:512k(u-boot)," \ "128k(params)," \ "4m(kernel)," \ "-(rootfs);"
我们将u-boot分区设置为512kb,我们使用MiniTools下载u-boot、内核。
然后启动开发板,串口输出如下信息:
可以发现并没有找到内核,这里我们分析一下原因。
我们直接烧入购买开发板时出厂的程序,然后启动linux:
我们查看linux启动时输出的日志信息:
可以发现分区情况和我们设置的不一致。我们发现内核起始地址是在0x60000,那么我么修改我们的分区配置include/configs/smdk2440.h:
/* mtdparts command line support */ #define MTDIDS_DEFAULT "nand0=Mini2440-0" /* default mtd partition table */ #define MTDPARTS_DEFAULT "mtdparts=Mini2440-0:256k(u-boot)," \ "128k(params)," \ "4m(kernel)," \ "-(rootfs);"
同时修改u-boot环境变量的保存地址:
/* 保存环境变量到NOR FLASH */ #if 0 #define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x040000) #define CONFIG_ENV_IS_IN_FLASH #define CONFIG_ENV_SIZE 0x10000 #else /* 保存环境变量到NAND FLASH */ #define CONFIG_ENV_IS_IN_NAND /* U-Boot env in NAND Flash */ #define CONFIG_ENV_SIZE 0x20000 //128kb #define CONFIG_ENV_OFFSET 0x40000 //给uboot预留256kb
然后重新编译u-boot下载运行(需要注意的一点,如果我们内核为zImage,应该使用go命令启动,uImage才是使用bootm命令启动):
#define CONFIG_BOOTCOMMAND "nand read 0x30000000 kernel; go 0x30000000"
再次编译u-boot:直接运行如下命令即可:
make clean // 只清除.o文件和可执行文件 make distclean // 清理所有生成的文件,包括配置文件 make smdk2440_defconfig make ARCH=arm CROSS_COMPILE=arm-linux- V=1
下载u-boot.bin、zImage_P35到NAND FLASH运行,发现已经开始解压内核了,虽然还有其他错误,但是我们可以先忽略。
这里在将内核从NAND FLASH 读取出来时出现这样的错误
NAND read from offset 60000 failed -74
我们再u-boot命令行模式下,尝试读取NAND 0x60000地址处数据,加载到内存:
SMDK2440 # nand read 0x30000000 0x60000 0x500000 NAND read: device 0 offset 0x60000, size 0x400000 NAND read from offset 60000 failed -74 0 bytes read: ERROR
发现出现同样的错误。我们参考u-boot_2010.6 nandflash驱动彻底分析中的分析。下面进行具体分析nand read执行流程。
1.3.2 nand read 错误码-74
执行nand read 命令后,其实是执行了nand_read_skip_bad(nand, off, &size,(u_char *)addr);
跳过坏块读函数的参数简单明了,从哪读,读到哪去,读多少,以及一个公共句柄(包含nand的信息,例如有多少个块,块大小等)
我们定位到nand_read_skip_bad函数,位于drivers/mtd/nand/nand_util.c文件:
/** * nand_read_skip_bad: * * Read image from NAND flash. * Blocks that are marked bad are skipped and the next block is read * instead as long as the image is short enough to fit even after * skipping the bad blocks. Due to bad blocks we may not be able to * perform the requested read. In the case where the read would extend * beyond the end of the NAND device, both length and actual (if not * NULL) are set to 0. In the case where the read would extend beyond * the limit we are passed, length is set to 0 and actual is set to the * required length. * * @param nand NAND device * @param offset offset in flash * @param length buffer length, on return holds number of read bytes * @param actual set to size required to read length worth of buffer or 0 * on error, if not NULL * @param lim maximum size that actual may be in order to not exceed the * buffer * @param buffer buffer to write to * @return 0 in case of success */ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, size_t *actual, loff_t lim, u_char *buffer) { int rval; size_t left_to_read = *length; size_t used_for_read = 0; u_char *p_buffer = buffer; int need_skip; if ((offset & (nand->writesize - 1)) != 0) { printf("Attempt to read non page-aligned data\n"); *length = 0; if (actual) *actual = 0; return -EINVAL; } need_skip = check_skip_len(nand, offset, *length, &used_for_read); if (actual) *actual = used_for_read; if (need_skip < 0) { printf("Attempt to read outside the flash area\n"); *length = 0; return -EINVAL; } if (used_for_read > lim) { puts("Size of read exceeds partition or device limit\n"); *length = 0; return -EFBIG; } if (!need_skip) { rval = nand_read(nand, offset, length, buffer); if (!rval || rval == -EUCLEAN) return 0; *length = 0; printf("NAND read from offset %llx failed %d\n", offset, rval); return rval; } while (left_to_read > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t read_length; WATCHDOG_RESET(); if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { printf("Skipping bad block 0x%08llx\n", offset & ~(nand->erasesize - 1)); offset += nand->erasesize - block_offset; continue; } if (left_to_read < (nand->erasesize - block_offset)) read_length = left_to_read; else read_length = nand->erasesize - block_offset; rval = nand_read(nand, offset, &read_length, p_buffer); if (rval && rval != -EUCLEAN) { printf("NAND read from offset %llx failed %d\n", offset, rval); *length -= left_to_read; return rval; } left_to_read -= read_length; offset += read_length; p_buffer += read_length; } return 0; }
这里会调用nand_read从nand读取数据,而nand_read又调用nand_do_read_ops,该函数位于drivers/mtd/nand/nand_base.c:
/** * nand_do_read_ops - [INTERN] Read data with ECC * @mtd: MTD device structure * @from: offset to read from * @ops: oob ops structure * * Internal function. Called with chip held. */ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; int use_bufpoi; unsigned int max_bitflips = 0; int retry_mode = 0; bool ecc_fail = false; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); buf = ops->datbuf; oob = ops->oobbuf; oob_required = oob ? 1 : 0; while (1) { unsigned int ecc_failures = mtd->ecc_stats.failed; WATCHDOG_RESET(); bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); if (!aligned) use_bufpoi = 1; else use_bufpoi = 0; /* Is the current page in the buffer? */ if (realpage != chip->pagebuf || oob) { bufpoi = use_bufpoi ? chip->buffers->databuf : buf; if (use_bufpoi && aligned) pr_debug("%s: using read bounce buffer for buf@%p\n", __func__, buf); read_retry: chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); /* * Now read the page into the buffer. Absent an error, * the read methods return max bitflips per ecc step. */ if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, oob_required, page); else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi, page); else ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); if (ret < 0) { if (use_bufpoi) /* Invalidate page cache */ chip->pagebuf = -1; break; } max_bitflips = max_t(unsigned int, max_bitflips, ret); /* Transfer not aligned data */ if (use_bufpoi) { if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - ecc_failures) && (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; chip->pagebuf_bitflips = ret; } else { /* Invalidate page cache */ chip->pagebuf = -1; } memcpy(buf, chip->buffers->databuf + col, bytes); } if (unlikely(oob)) { int toread = min(oobreadlen, max_oobsize); if (toread) { oob = nand_transfer_oob(chip, oob, ops, toread); oobreadlen -= toread; } } if (chip->options & NAND_NEED_READRDY) { /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else nand_wait_ready(mtd); } if (mtd->ecc_stats.failed - ecc_failures) { if (retry_mode + 1 < chip->read_retries) { retry_mode++; ret = nand_setup_read_retry(mtd, retry_mode); if (ret < 0) break; /* Reset failures; retry */ mtd->ecc_stats.failed = ecc_failures; goto read_retry; } else { /* No more retry modes; real failure */ ecc_fail = true; } } buf += bytes; } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; max_bitflips = max_t(unsigned int, max_bitflips, chip->pagebuf_bitflips); } readlen -= bytes; /* Reset to retry mode 0 */ if (retry_mode) { ret = nand_setup_read_retry(mtd, 0); if (ret < 0) break; retry_mode = 0; } if (!readlen) break; /* For subsequent reads align to page boundary */ col = 0; /* Increment page address */ realpage++; page = realpage & chip->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } } chip->select_chip(mtd, -1); ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; if (ret < 0) return ret; if (ecc_fail) return -EBADMSG; return max_bitflips; }
EBADMSG定义为74,不难看出最终执行到了:
if (ecc_fail) return -EBADMSG;
所以抛出了异常状态码-74。定位到ecc_fail设置为true的代码:
if (mtd->ecc_stats.failed - ecc_failures) { if (retry_mode + 1 < chip->read_retries) { retry_mode++; ret = nand_setup_read_retry(mtd, retry_mode); if (ret < 0) break; /* Reset failures; retry */ mtd->ecc_stats.failed = ecc_failures; goto read_retry; } else { /* No more retry modes; real failure */ ecc_fail = true; } }
那么大致阅读一下这个代码,我猜想应该是nand_do_read_ops在执行下面函数时,出现了问题:
ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page);
这里我直接在vs code搜索read_page,我们很快定位到了nand_scan_tail这个函数,这个函数也位于drivers/mtd/nand/nand_base.c文件,
我们直接定位到下面这段代码:
/** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * * This is the second phase of the normal nand_scan() function. It fills out * all the uninitialized function pointers with the defaults and scans for a * bad block table if appropriate. */ int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_buffers *nbuf; ... /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one. */ if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: ecc->layout = &nand_oob_8; break; case 16: ecc->layout = &nand_oob_16; break; case 64: ecc->layout = &nand_oob_64; break; case 128: ecc->layout = &nand_oob_128; break; default: pr_warn("No oob scheme defined for oobsize %d\n", mtd->oobsize); BUG(); } } if (!chip->write_page) chip->write_page = nand_write_page; /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ switch (ecc->mode) { case NAND_ECC_HW_OOB_FIRST: ... case NAND_ECC_HW: ... case NAND_ECC_HW_SYNDROME: ... case NAND_ECC_SOFT: ecc->calculate = nand_calculate_ecc; ecc->correct = nand_correct_data; ecc->read_page = nand_read_page_swecc; ecc->read_subpage = nand_read_subpage; ecc->write_page = nand_write_page_swecc; ecc->read_page_raw = nand_read_page_raw; ecc->write_page_raw = nand_write_page_raw; ecc->read_oob = nand_read_oob_std; ecc->write_oob = nand_write_oob_std; if (!ecc->size) ecc->size = 256; ecc->bytes = 3; ecc->strength = 1; break; case NAND_ECC_SOFT_BCH: ... case NAND_ECC_NONE: ... default: pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); BUG(); } ... return 0; }
还记的在Mini440之uboot移植之实践NAND启动(四)中我们介绍过board_nand_init函数,该函数开启了软件ecc校验:
nand->ecc.mode = NAND_ECC_SOFT;
因此:ecc->read_page = nand_read_page_swecc,nand_read_page_swecc在drivers/mtd/nand/nand_base.c文件中定义:
/** * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; unsigned int max_bitflips = 0; chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; eccsteps = chip->ecc.steps; p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { mtd->ecc_stats.corrected += stat; max_bitflips = max_t(unsigned int, max_bitflips, stat); } } return max_bitflips; }
这个带有软件ecc校验的NAND页数据读取函数,这个函数在从NAND读完一页数据后会进行软件ecc校验,如果校验失败就会修改mtd->ecc_stats.failed,从而导致后面执行了:
ecc_fail = true;
在连续失败chip->read_retry次后,将会跳出循环,不再进行NAND数据读取。
为了不抛出-74这个异常,我们可以尝试关闭ecc校验,通过修改board_nand_init,注释掉下面代码,即关闭软件ecc:
// nand->ecc.mode = NAND_ECC_SOFT;
此时nand->ecc.mode默认采用NAND_ECC_NONE:
case NAND_ECC_NONE: pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); ecc->read_page = nand_read_page_raw; ecc->write_page = nand_write_page_raw; ecc->read_oob = nand_read_oob_std; ecc->read_page_raw = nand_read_page_raw; ecc->write_page_raw = nand_write_page_raw; ecc->write_oob = nand_write_oob_std; ecc->size = mtd->writesize; ecc->bytes = 0; ecc->strength = 0; break;
此时在进行按页读写时就不会进行软件ecc校验了。
/** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; }
我们编译u-boot、烧写进入NAND,再次启动u-boot,引导Linux启动,此时错误信息变成了:
内核在运行的时候ecc校验失败了。后面我们会自己生成uImage,而不是使用出厂自带的zImage。
1.4 测试NAND读取功能
在u-boot引导内核启动前,我们尝试按下键盘任意键,进入u-boot命令行模式。
查看NAND第一页数据:
SMDK2440 # nand dump 0x00 0x800 Page 00000000 dump: be 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 60 00 f0 33 c0 00 f0 33 20 01 f0 33 80 01 f0 33 e0 01 f0 33 40 02 f0 33 a0 02 f0 33 ef be ad de de c0 ad 0b 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 28 d0 1f e5 00 e0 8d e5 00 e0 4f e1 04 e0 8d e5 13 d0 a0 e3 0d f0 69 e1 0f e0 a0 e1 0e f0 b0 e1 48 d0 4d e2 ff 1f 8d e8 50 20 1f e5 0c 00 92 e8 48 00 8d e2 34 50 8d e2 0e 10 a0 e1 0f 00 85 e8 0d 00 a0 e1 52 04 00 eb 00 00 a0 e1 00 00 a0 e1 ...
可以直接读取NAND FLASH数据到内存来验证该数据:
mw.b 0x30000000 ff 0x800 #清除内存 nand read 0x30000000 0x00 0x800 #读数据到内存 md 0x30000000 0x800 #显示内存
运行结果如下:
SMDK2440 # mw.b 0x30000000 ff 0x800 SMDK2440 # nand read 0x30000000 0x00 0x800 NAND read: device 0 offset 0x0, size 0x800 2048 bytes read: OK SMDK2440 # md 0x30000000 0x800 30000000: ea0000be e59ff014 e59ff014 e59ff014 ................ 30000010: e59ff014 e59ff014 e59ff014 e59ff014 ................ 30000020: 33f00060 33f000c0 33f00120 33f00180 `..3...3 ..3...3 30000030: 33f001e0 33f00240 33f002a0 deadbeef ...3@..3...3.... 30000040: 0badc0de e1a00000 e1a00000 e1a00000 ................ 30000050: e1a00000 e1a00000 e1a00000 e1a00000 ................ 30000060: e51fd028 e58de000 e14fe000 e58de004 (.........O..... 30000070: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i......... 30000080: e24dd048 e88d1fff e51f2050 e892000c H.M.....P ...... 30000090: e28d0048 e28d5034 e1a0100e e885000f H...4P.......... 300000a0: e1a0000d eb000452 e1a00000 e1a00000 ....R........... ...
可以看到nand dump与nand read指定地址到内存中的数据是一样的。这也表明在关闭ecc校验的情况下,我们能够断定移植的u-boot可以对NAND FLASH进行正确的读和写操作。
二、移植linux内核
2.1 内核源码下载
内核源码下载地址为:https://www.kernel.org/,这里我们不要下载最新的,最新的里面已经没有s3c24xx系列的默认配置了:
也可以到内核镜像网址下载https://mirrors.edge.kernel.org/pub/linux/kernel/,这里下载速度更快。
如果下载速度太慢,无法下载,提供另一个链接:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/。
我们这里下载linux-5.2.8版本,虚拟机ubuntu系统运行:
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.2.8.tar.gz
将源码解压:
tar -zxvf linux-5.2.8.tar.gz
2.2 linux内核目录结构
解压就可以得到全部linux内核源程序,目录结构如下:
下面列出了顶层目录下各级目录存放原则:
- arch:体系结构相关的代码,比如 arm、 avr32、 m68k 等,我们现在用的是 ARM 芯片,所以只需要关心 arm 文件夹即可;
- block:块设备相关的通用函数;
- crypto:常用加密和散列算法(如 AES、SHA等),还有一些压缩和CRC校验算法;
- drivers:所有的设备驱动程序,里面每一个子目录对应一类驱动程序,比如 drivers/block/ 为块设备驱动程序,drivers/char/为字符设备驱动程序,drivers/mtd/为nor flash、nand flash 等存储设备的驱动程序;
- Documentation:Linux内核的使用帮助文档;
- fs:Linux 支持的文件系统的代码,每个子目录对应一种文件系统,比如 fs/jffs2/、fs/ext2/、fs/ext4/等;
- include:内核头文件,有基本头文件(存放在 include/linux/目录下)、各种驱动或功能部件的头文件(如 include/media/、include/video/、include/net等)、 各种体系相关的头文件(如 include/asm-generic/等);
- init:内核的初始化代码(不是系统的引导代码),其中的 main.c 文件中的 start_kernel 函数是内核引导后运行的第一个函数;
- ipc:进程间通信的代码;
- kernel:内核管理的核心代码;
- lib:内核用到的一些库函数代码,如 crc32.c、string.c、shal.c等,这类文件夹中的内容移植时基本不用管;
- mm:内存管理代码;
- net:网络支持代码,每个子目录对应子网络的一个方面;
- samples:一些示例程序,如断点调试,功能测试等;
- scripts:用于配置、编译内核的脚本文件
- security:安全、密钥相关的代码;
- sound:音频设备驱动程序;
- tools:工具类代码,比如 USB 传输等,通常会将 u-boot 下生成的mkimage工具放到此目录下;
- usr:忽略即可;
- virt:忽略即可;
2.3 配置Makefile
修改顶层的 Makefile,打开 Makefile 文件,找到下面语句:
ARCH ?= $(SUBARCH)
修改为:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
其中,ARCH 是指定目标平台为arm,CROSS_COMPILE是指定交叉编译器,这里指定的 是系统默认的交叉编译器,如要使用其它的,则要把编译器的全路径在这里写出。
2.4 内核s3c2440_defconfig配置
接下来要做的就是内核配置、编译了。单板的默认配置文件在arch/arm/configs 目录下,如果没有2440相关的默认配置,可以选择比较相近的2410的配置修改,这里我直接选择s3c2410_defconfig,并没有选择mini2440_defconfig。
配置文件s3c2410_defconfig支持很多单板,包括2440、2410,其定义如下:
CONFIG_SYSVIPC=y CONFIG_IKCONFIG=m CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y CONFIG_BSD_DISKLABEL=y CONFIG_SOLARIS_X86_PARTITION=y CONFIG_ARCH_S3C24XX=y CONFIG_CPU_S3C2412=y CONFIG_CPU_S3C2416=y CONFIG_CPU_S3C2440=y CONFIG_CPU_S3C2442=y CONFIG_CPU_S3C2443=y CONFIG_MACH_AML_M5900=y CONFIG_ARCH_BAST=y CONFIG_ARCH_H1940=y CONFIG_MACH_N30=y CONFIG_MACH_OTOM=y CONFIG_MACH_QT2410=y CONFIG_ARCH_SMDK2410=y CONFIG_MACH_TCT_HAMMER=y CONFIG_MACH_VR1000=y CONFIG_MACH_JIVE=y CONFIG_MACH_SMDK2412=y CONFIG_MACH_VSTMS=y CONFIG_MACH_SMDK2416=y CONFIG_MACH_ANUBIS=y CONFIG_MACH_AT2440EVB=y CONFIG_MACH_MINI2440=y CONFIG_MACH_NEXCODER_2440=y CONFIG_MACH_OSIRIS=y CONFIG_MACH_OSIRIS_DVS=m CONFIG_MACH_RX3715=y CONFIG_ARCH_S3C2440=y # 会链接mach-smdk2440.o CONFIG_MACH_NEO1973_GTA02=y CONFIG_MACH_RX1950=y CONFIG_MACH_SMDK2443=y CONFIG_S3C_ADC=y CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_CMDLINE="root=/dev/hda1 ro init=/bin/bash console=ttySAC0" CONFIG_FPE_NWFPE=y CONFIG_FPE_NWFPE_XP=y CONFIG_APM_EMULATION=m CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_XFRM_USER=m CONFIG_NET_KEY=m CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y CONFIG_NET_IPIP=m CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m CONFIG_TCP_CONG_ADVANCED=y CONFIG_TCP_CONG_HSTCP=m CONFIG_TCP_CONG_HYBLA=m CONFIG_TCP_CONG_SCALABLE=m CONFIG_TCP_CONG_LP=m CONFIG_TCP_CONG_VENO=m CONFIG_TCP_CONG_YEAH=m CONFIG_TCP_CONG_ILLINOIS=m CONFIG_IPV6_ROUTER_PREF=y CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m CONFIG_IPV6_MIP6=m CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m CONFIG_IPV6_TUNNEL=m CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CONNTRACK_AMANDA=m CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_H323=m CONFIG_NF_CONNTRACK_IRC=m CONFIG_NF_CONNTRACK_NETBIOS_NS=m CONFIG_NF_CONNTRACK_PPTP=m CONFIG_NF_CONNTRACK_SANE=m CONFIG_NF_CONNTRACK_SIP=m CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NF_CT_NETLINK=m CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_CONNMARK=m CONFIG_NETFILTER_XT_TARGET_LED=m CONFIG_NETFILTER_XT_TARGET_LOG=m CONFIG_NETFILTER_XT_TARGET_MARK=m CONFIG_NETFILTER_XT_TARGET_NFLOG=m CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m CONFIG_NETFILTER_XT_TARGET_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_CLUSTER=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m CONFIG_NETFILTER_XT_MATCH_CONNMARK=m CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m CONFIG_NETFILTER_XT_MATCH_DCCP=m CONFIG_NETFILTER_XT_MATCH_DSCP=m CONFIG_NETFILTER_XT_MATCH_ESP=m CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m CONFIG_NETFILTER_XT_MATCH_HELPER=m CONFIG_NETFILTER_XT_MATCH_IPRANGE=m CONFIG_NETFILTER_XT_MATCH_LENGTH=m CONFIG_NETFILTER_XT_MATCH_LIMIT=m CONFIG_NETFILTER_XT_MATCH_MAC=m CONFIG_NETFILTER_XT_MATCH_MARK=m CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m CONFIG_NETFILTER_XT_MATCH_OWNER=m CONFIG_NETFILTER_XT_MATCH_POLICY=m CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m CONFIG_NETFILTER_XT_MATCH_QUOTA=m CONFIG_NETFILTER_XT_MATCH_RATEEST=m CONFIG_NETFILTER_XT_MATCH_REALM=m CONFIG_NETFILTER_XT_MATCH_RECENT=m CONFIG_NETFILTER_XT_MATCH_SCTP=m CONFIG_NETFILTER_XT_MATCH_STATE=m CONFIG_NETFILTER_XT_MATCH_STATISTIC=m CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_TIME=m CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_IP_VS=m CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_NETMAP=m CONFIG_IP_NF_TARGET_REDIRECT=m CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_NF_CONNTRACK_IPV6=m CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_EUI64=m CONFIG_IP6_NF_MATCH_FRAG=m CONFIG_IP6_NF_MATCH_OPTS=m CONFIG_IP6_NF_MATCH_HL=m CONFIG_IP6_NF_MATCH_IPV6HEADER=m CONFIG_IP6_NF_MATCH_MH=m CONFIG_IP6_NF_MATCH_RT=m CONFIG_IP6_NF_TARGET_HL=m CONFIG_IP6_NF_FILTER=m CONFIG_IP6_NF_TARGET_REJECT=m CONFIG_IP6_NF_MANGLE=m CONFIG_IP6_NF_RAW=m CONFIG_BT=m CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y CONFIG_BT_BNEP=m CONFIG_BT_BNEP_MC_FILTER=y CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=m CONFIG_BT_HCIUART=m CONFIG_BT_HCIUART_BCSP=y CONFIG_BT_HCIUART_LL=y CONFIG_BT_HCIBCM203X=m CONFIG_BT_HCIBPA10X=m CONFIG_BT_HCIBFUSB=m CONFIG_BT_HCIVHCI=m CONFIG_CFG80211=m CONFIG_MAC80211=m CONFIG_MAC80211_MESH=y CONFIG_MAC80211_LEDS=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_MTD=y CONFIG_MTD_REDBOOT_PARTS=y CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_JEDECPROBE=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_ROM=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_S3C2410=y CONFIG_PARPORT=y CONFIG_PARPORT_PC=m CONFIG_PARPORT_AX88796=m CONFIG_PARPORT_1284=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_ATA_OVER_ETH=m CONFIG_EEPROM_AT24=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=m CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_CHR_DEV_SG=y CONFIG_CHR_DEV_SCH=m CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_SCAN_ASYNC=y CONFIG_ATA=y CONFIG_PATA_PLATFORM=y CONFIG_NETDEVICES=y CONFIG_DM9000=y CONFIG_INPUT_EVDEV=y CONFIG_MOUSE_APPLETOUCH=m CONFIG_MOUSE_BCM5974=m CONFIG_INPUT_JOYSTICK=y CONFIG_JOYSTICK_ANALOG=m CONFIG_JOYSTICK_A3D=m CONFIG_JOYSTICK_ADI=m CONFIG_JOYSTICK_COBRA=m CONFIG_JOYSTICK_GF2K=m CONFIG_JOYSTICK_GRIP=m CONFIG_JOYSTICK_GRIP_MP=m CONFIG_JOYSTICK_GUILLEMOT=m CONFIG_JOYSTICK_INTERACT=m CONFIG_JOYSTICK_SIDEWINDER=m CONFIG_JOYSTICK_TMDC=m CONFIG_JOYSTICK_IFORCE=m CONFIG_JOYSTICK_MAGELLAN=m CONFIG_JOYSTICK_SPACEORB=m CONFIG_JOYSTICK_SPACEBALL=m CONFIG_JOYSTICK_STINGER=m CONFIG_JOYSTICK_TWIDJOY=m CONFIG_JOYSTICK_ZHENHUA=m CONFIG_JOYSTICK_DB9=m CONFIG_JOYSTICK_GAMECON=m CONFIG_JOYSTICK_TURBOGRAFX=m CONFIG_JOYSTICK_JOYDUMP=m CONFIG_JOYSTICK_XPAD=m CONFIG_JOYSTICK_XPAD_FF=y CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_USB_COMPOSITE=m CONFIG_INPUT_MISC=y CONFIG_INPUT_ATI_REMOTE2=m CONFIG_INPUT_KEYSPAN_REMOTE=m CONFIG_INPUT_POWERMATE=m CONFIG_INPUT_YEALINK=m CONFIG_INPUT_CM109=m CONFIG_INPUT_UINPUT=m CONFIG_INPUT_GPIO_ROTARY_ENCODER=m CONFIG_SERIAL_NONSTANDARD=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=8 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_SAMSUNG=y CONFIG_SERIAL_SAMSUNG_CONSOLE=y CONFIG_SERIAL_DEV_BUS=m CONFIG_PRINTER=y CONFIG_PPDEV=y CONFIG_HW_RANDOM=y CONFIG_I2C_CHARDEV=m CONFIG_I2C_S3C2410=y CONFIG_I2C_SIMTEC=y CONFIG_SPI=y CONFIG_SPI_GPIO=m CONFIG_SPI_S3C24XX=m CONFIG_SPI_SPIDEV=m CONFIG_SPI_TLE62X0=m CONFIG_SENSORS_LM75=m CONFIG_SENSORS_LM78=m CONFIG_SENSORS_LM85=m CONFIG_WATCHDOG=y CONFIG_S3C2410_WATCHDOG=y CONFIG_MFD_SM501=y CONFIG_TPS65010=y CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_S3C2410=y CONFIG_FB_SM501=y CONFIG_BACKLIGHT_PWM=m CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_SEQUENCER=m CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m CONFIG_SND_SEQUENCER_OSS=y CONFIG_SND_VERBOSE_PRINTK=y # CONFIG_SND_DRIVERS is not set # CONFIG_SND_ARM is not set # CONFIG_SND_SPI is not set CONFIG_SND_USB_AUDIO=m CONFIG_SND_USB_CAIAQ=m CONFIG_SND_SOC=y # CONFIG_USB_HID is not set CONFIG_USB=y CONFIG_USB_MON=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_ACM=m CONFIG_USB_PRINTER=m CONFIG_USB_WDM=m CONFIG_USB_STORAGE=m CONFIG_USB_STORAGE_DATAFAB=m CONFIG_USB_STORAGE_FREECOM=m CONFIG_USB_STORAGE_ISD200=m CONFIG_USB_STORAGE_USBAT=m CONFIG_USB_STORAGE_SDDR09=m CONFIG_USB_STORAGE_SDDR55=m CONFIG_USB_STORAGE_JUMPSHOT=m CONFIG_USB_STORAGE_ALAUDA=m CONFIG_USB_STORAGE_ONETOUCH=m CONFIG_USB_STORAGE_KARMA=m CONFIG_USB_STORAGE_CYPRESS_ATACB=m CONFIG_USB_MDC800=m CONFIG_USB_MICROTEK=m CONFIG_USB_USS720=m CONFIG_USB_SERIAL=y CONFIG_USB_SERIAL_GENERIC=y CONFIG_USB_SERIAL_FTDI_SIO=y CONFIG_USB_SERIAL_NAVMAN=m CONFIG_USB_SERIAL_PL2303=y CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m CONFIG_USB_ADUTUX=m CONFIG_USB_SEVSEG=m CONFIG_USB_RIO500=m CONFIG_USB_LEGOTOWER=m CONFIG_USB_LCD=m CONFIG_USB_CYPRESS_CY7C63=m CONFIG_USB_CYTHERM=m CONFIG_USB_IDMOUSE=m CONFIG_USB_FTDI_ELAN=m CONFIG_USB_APPLEDISPLAY=m CONFIG_USB_LD=m CONFIG_USB_TRANCEVIBRATOR=m CONFIG_USB_IOWARRIOR=m CONFIG_USB_TEST=m CONFIG_MMC=y CONFIG_SDIO_UART=m CONFIG_MMC_TEST=m CONFIG_MMC_SDHCI=m CONFIG_MMC_SPI=m CONFIG_MMC_S3C=y CONFIG_LEDS_S3C24XX=m CONFIG_LEDS_PCA9532=m CONFIG_LEDS_GPIO=m CONFIG_LEDS_PCA955X=m CONFIG_LEDS_DAC124S085=m CONFIG_LEDS_PWM=m CONFIG_LEDS_BD2802=m CONFIG_LEDS_TRIGGER_TIMER=m CONFIG_LEDS_TRIGGER_HEARTBEAT=m CONFIG_LEDS_TRIGGER_GPIO=m CONFIG_LEDS_TRIGGER_DEFAULT_ON=m CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_S3C=y CONFIG_DMADEVICES=y CONFIG_S3C24XX_DMAC=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT2_FS_SECURITY=y CONFIG_EXT3_FS=y CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_AUTOFS4_FS=m CONFIG_FUSE_FS=m CONFIG_ISO9660_FS=y CONFIG_JOLIET=y CONFIG_UDF_FS=m CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_NTFS_FS=m CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_CONFIGFS_FS=m CONFIG_JFFS2_FS=y CONFIG_JFFS2_SUMMARY=y CONFIG_CRAMFS=y CONFIG_SQUASHFS=m CONFIG_ROMFS_FS=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_ROOT_NFS=y CONFIG_NFSD=m CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y CONFIG_CIFS=m CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m CONFIG_NLS_CODEPAGE_775=m CONFIG_NLS_CODEPAGE_850=y CONFIG_NLS_CODEPAGE_852=m CONFIG_NLS_CODEPAGE_855=m CONFIG_NLS_CODEPAGE_857=m CONFIG_NLS_CODEPAGE_860=m CONFIG_NLS_CODEPAGE_861=m CONFIG_NLS_CODEPAGE_862=m CONFIG_NLS_CODEPAGE_863=m CONFIG_NLS_CODEPAGE_864=m CONFIG_NLS_CODEPAGE_865=m CONFIG_NLS_CODEPAGE_866=m CONFIG_NLS_CODEPAGE_869=m CONFIG_NLS_CODEPAGE_936=m CONFIG_NLS_CODEPAGE_950=m CONFIG_NLS_CODEPAGE_932=m CONFIG_NLS_CODEPAGE_949=m CONFIG_NLS_CODEPAGE_874=m CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m CONFIG_NLS_ISO8859_4=m CONFIG_NLS_ISO8859_5=m CONFIG_NLS_ISO8859_6=m CONFIG_NLS_ISO8859_7=m CONFIG_NLS_ISO8859_9=m CONFIG_NLS_ISO8859_13=m CONFIG_NLS_ISO8859_14=m CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=m CONFIG_DEBUG_INFO=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_USER=y CONFIG_DEBUG_LL=y
在linux内核根目录下执行如下命令,执行完之后会在内核根目录下生成默认配置文件.config:
make s3c2410_defconfig
然后可以通过make menuconfig修改配置:
make menuconfig
2.4.1 配置EABI编译属性
因为arm-none-linux-gnueabi 4.8.3(这个版本的编译器后面会介绍)使用了EABI方式,所以这就需要内核同样配置EABI编译属性:
Kernel Features ---> [*] Use the ARM EABI to compile the kernel [*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL)
修改完之后,保存文件,输入文件名:
在当前路径下生成s3c2440_defconfig:
存档:
mv s3c2440_defconfig ./arch/arm/configs/
存档之后,下次如果需要重新编译配置直接运行make s3c2440_defconfig,避免了重新进行menuconfig配置。
在arch/arm/mach-s3c24xx目录下有个mach-smdk2440.c文件,这个文件是三星厂商提供的smdk2440开发版对应的示例程序。后面我们会对这个文件进行修改来适配Mini2440开发板。
2.5 源码修改
2.5.1 修改时钟频率
所以修改 arch/arm/mach-s3c24xx/mach-smdk2440.c:
static void __init smdk2440_init_time(void) { s3c2440_init_clocks(12000000); samsung_timer_init(); }
打开 arch/arm/mach-s3c24xx/common-smdk.c 文件,仿照u-boot的分区,修改代码如下:
/* NAND parititon from 2.4.18-swl5 */ static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "u-boot", .size = SZ_256K, .offset = 0, }, [1] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "kermel", .offset = MTDPART_OFS_APPEND, .size = SZ_4M, }, [3] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } };
256MB大小的NAND被分成四个分区:
- 0x00000000~0x00040000:256kb存放u-boot;
- 0x00040000~0x00060000: 128kb存放环境变量;
- 0x00060000~0x00460000:4MB存放linux内核;
- 0x00460000~0x10000000:剩下空间存放根文件系统;
上面部分宏的定义位于 include/linux/mtd/partitions.h 文件中,如下所示:
#define MTDPART_OFS_RETAIN (-3) #define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_APPEND (-1) #define MTDPART_SIZ_FULL (0)
2.5.3 关闭ecc软件校验
linux内核在启动的时候回对NAND FLASH进行ecc校验,如果有坏块将会导致ecc检验不通过,导致内核启动失败:
print_req_error: I/O error, dev mtdblock3, sector 0 flags 0 Buffer I/O error on dev mtdblock3, logical block 0, async page read __nand_correct_data: uncorrectable ECC error print_req_error: I/O error, dev mtdblock3, sector 8 flags 0 Buffer I/O error on dev mtdblock3, logical block 1, async page read __nand_correct_data: uncorrectable ECC error print_req_error: I/O error, dev mtdblock3, sector 16 flags 0 Buffer I/O error on dev mtdblock3, logical block 2, async page read print_req_error: I/O error, dev mtdblock3, sector 24 flags 0 Buffer I/O error on dev mtdblock3, logical block 3, async page read print_req_error: I/O error, dev mtdblock3, sector 0 flags 0 FAT-fs (mtdblock3): unable to read boot sector VFS: Cannot open root device "mtdblock3" or unknown-block(31,3): error -5 Please append a correct "root=" boot option; here are the available partitions:
解决方法是禁止NAND FLASH进行软件ecc校验。
修改arch/arm/mach-s3c24xx/common-smdk.c文件:
/* choose a set of timings which should suit most 512Mbit * chips and beyond. */ static struct s3c2410_platform_nand smdk_nand_info = { .tacls = 20, .twrph0 = 60, .twrph1 = 20, .nr_sets = ARRAY_SIZE(smdk_nand_sets), .sets = smdk_nand_sets, .ecc_mode = NAND_ECC_SOFT, };
将NAND_ECC_SOFT改为NAND_ECC_NONE,这个去掉ecc校验的问题,在内核中明确说明是不建议这样做的,因为这样就等于忽略了对NAND FLASH坏块的检测。
2.5.4 设置matchid
linux内核在启动时,是通过u-boot传入的机器码确定应启动哪种目标平台的。
如何确定linux内核机器id呢?在linux-5.2.8/arch/arm/mach-s3c24xx/mach-smdk2440.c中,struct machine_desc定义如下:
MACHINE_START(S3C2440, "SMDK2440") /* Maintainer: Ben Dooks <ben-linux@fluff.org> */ .atag_offset = 0x100, .init_irq = s3c2440_init_irq, .map_io = smdk2440_map_io, .init_machine = smdk2440_machine_init, .init_time = smdk2440_init_time, MACHINE_END
显然,SMDK2440使用的机器id是MACH_TYPE_S3C2440。具体的数字可以在arch/arm/tools/mach-types文件中找到(kernel在编译过程中会根据此文件生成相应的头文件供源码使用),具体数字是0x16A。
s3c2440 ARCH_S3C2440 S3C2440 362
这里需要修改为:
s3c2440 ARCH_S3C2440 S3C2440 168
168是u-boot里设置的,这个机器码需要跟u-boot中的机器码相对应,要不然u-boot无法引导启动内核,如果你不知道uboot中的机器码是多少,在uboot命令行中输入命令bdinfo查看。
SMDK2440 # bdinfo
arch_number = 0x000000A8
boot_params = 0x30000100
DRAM bank = 0x00000000
-> start = 0x30000000
-> size = 0x04000000
eth0name = dm9000
ethaddr = 08:00:3e:26:0a:5b
current eth = dm9000
ip_addr = 192.168.0.188
baudrate = 115200 bps
TLB addr = 0x33FF0000
relocaddr = 0x33F00000
reloc off = 0x00000000
irq_sp = 0x33AFFEF0
sp start = 0x33AFFEE0
2.6 编译内核制作uImage
先运行如下命令,查看编译是否出错:
make V=s
如果出现下面错误:
原因是libssl-dev没有安装,使用sudo apt-get install libssl-dev来安装libssl-dev:
sudo apt-get install libssl-dev
如果出现类似下面的错误:
cc1: error: unrecognized command line option "..."
一般是由于较新的内核使用了新版本交叉编译器的特性,而我本地安装的交叉编译器版本较低导致,需要升级版本,这个单独小节介绍:
在内核根路径下运行命令:
make V=1 uImage
出现如下错误:
说明缺少 mkimage ,有两种解决办法:
- 利用uboot生成mkimage工具,然后拷贝到/usr/bin 目录下
- 输入 sudo apt-get install u-boot-tools 命令在线安装;
本文选择第二种方法,输入命令:
sudo apt-get install u-boot-tools
2.7 arm-linux-gcc 4.8.3交叉编译环境安装(内核编译失败安装)
我之前使用的为4.3.2版本,在编译高版本linux时出现错误,这里我将会将交叉编译环境升级到4.8.3:
编译器可以在ARM官网下载:https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads ,或者到http://releases.linaro.org/components/toolchain/binaries/下载。
选择编译器时有一点需要注意,我们选择的是arm-none-linux-guneabi-gcc编译器:
- arm-none-eabi-gcc (ARM architecture,no vendor,not target an operating system,complies with the ARM EABI): 用于编译 ARM 架构的裸机系统(包括 ARM Linux 的 boot、kernel,不适用编译 Linux 应用 Application), 一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数, 比如fork(2),它使用的是 newlib 这个专用于嵌入式系统的C库。
- arm-none-linux-gnueabi-gcc (ARM architecture, no vendor, creates binaries that run on the Linux operating system, and uses the GNU EABI) :主要用于基于ARM架构的Linux系统,可用于编译 ARM 架构的 u-boot、Linux内核、linux应用等。 arm-none-linux-gnueabi基于GCC,使用Glibc库,经过 Codesourcery 公司优化过推出的编译器。 arm-none-linux-gnueabi-xxx 交叉编译工具的浮点运算非常优秀。一般ARM9、ARM11、Cortex-A 内 核,带有 Linux 操作系统的会用到。
此外,关于EABI和ABI也是比较重要的,EABI(嵌入式应用二进制接口)和 ABI(应用程序二进制接口)都是二进制接口的标准,但是它们的应用场景不同;
- ABI 通常指操作系统、处理器等平台在二进制级别上的接口标准。它定义了操作系统内核、库函数和应用程序之间的接口规范,如函数调用、参数传递、返回值、系统调用等等。具体来说,一个 ABI 定义了编译器生成的可执行文件需要满足的规范,以便在特定平台和特定环境下运行。例如,x86 架构上的 Windows ABI 和 Linux ABI 在一些细节上可能有所不同,因为它们使用了不同的寄存器、调用约定等等。在不同 ABI 之间移植代码时,需要注意这些差异并进行相应的修改;
- EABI 则是专门针对嵌入式系统的 ABI 标准。与通用的 ABI 不同,EABI 更关注于嵌入式系统对于可移植性和交叉编译的需求。它定义了编译器生成的二进制代码与嵌入式系统之间的接口规范,包括函数调用、参数传递、返回值等方面,并支持软件浮点数运算。因此,通过 EABI 的规范,可以保证在不同的嵌入式系统之间生成的二进制代码具有良好的可移植性和兼容性。这样,在从一个嵌入式平台迁移到另一个平台时,只需要重新编译源代码即可,而不需要修改程序的源代码。
总之,ABI 和 EABI 都是二进制接口标准,它们都定义了二进制程序和操作系统之间的接口规范,但是应用场景不同。ABI 更关注通用平台上的二进制接口规范,而 EABI 则是专门针对嵌入式系统开发的二进制接口标准。
更多不同编译器之间的区别参考ARM交叉编译器GNUEABI、NONE-EABI、ARM-EABI、GNUEABIHF等的区别。
由于官网下载比较慢,这里我们直接从gitee上下载arm-none-linux-gnueabi 4.8.3:
git clone https://gitee.com/zyly2033/arm-none-linux-gnueabi-4.8.3.git
将其拷贝到/usr/local/arm路径下:
mv arm-none-linux-gnueabi-4.8.3 4.8.3 mv 4.8.3 /usr/local/arm
这样在/usr/local/arm下就有我们安装的三个不同版本的编译器:
接下来配置系统环境变量,把交叉编译工具链的路径添加到环境变量PATH中去,这样就可以在任何目录下使用这些工具:
编辑profile文件,添加环境变量:
vim /etc/profile
添加如下代码:
export PATH=$PATH:/usr/local/arm/4.8.3/bin
同时注释掉4.3.2、4.4.3版本的配置:
接下来使用命令:source /etc/profile,使修改后的profile文件生效。
由于在/usr/local/arm/4.8.3/bin下没有arm-linux-gcc、arm-linux-ld、arm-linux-strip链接,所以我们自己创建软链接:
ln -s arm-none-linux-gnueabi-gcc arm-linux-gcc ln -s arm-none-linux-gnueabi-ld arm-linux-ld ln -s arm-none-linux-gnueabi-objdump arm-linux-objdump ln -s arm-none-linux-gnueabi-objcopy arm-linux-objcopy ln -s arm-none-linux-gnueabi-strip arm-linux-strip ln -s arm-none-linux-gnueabi-cpp arm-linux-cpp ln -s arm-none-linux-gnueabi-ar arm-linux-ar ln -s arm-none-linux-gnueabi-as arm-linux-as ln -s arm-none-linux-gnueabi-strings arm-linux-strings ln -s arm-none-linux-gnueabi-readelf arm-linux-readelf ln -s arm-none-linux-gnueabi-size arm-linux-size ln -s arm-none-linux-gnueabi-c++ arm-linux-c++ ln -s arm-none-linux-gnueabi-gdb arm-linux-gdb ln -s arm-none-linux-gnueabi-nm arm-linux-nm
然后,使用命令:arm-linux-gcc -v查看当前交叉编译链工具的版本信息:
如果出现如下错误:
-bash: /usr/local/arm/4.8.3/bin/arm-linux-gcc: No such file or directory
这是因为64位的系统缺少32位的库导致的。解决办法:
sudo apt-get update sudo apt-get install lib32z1
这样就可以进行交叉编译了。
三、内核启动测试
如果我们需要修改linux内核代码重新编译,直接运行如下命令即可:
make s3c2440_defconfig make V=1 uImage
在 arch/arm/boot 目录下生成 uImage。
如果进行清理工作:
make clean // 只清除.o文件和可执行文件 make distclean // 清理所有生成的文件,包括配置文件
注意:记得编译linux内核时将arm-linux-gcc设置为4.8.3版本。
3.1 设置u-boot环境变量
首先将u-boot烧写进NAND。在使用u-boot启动之后,内核启动之前,还有两件事情要做:
设置启动参数,如果u-boot smdk2440.h中已经配置好了,这一步忽略:
set bootargs "console=ttySAC0,115200 root=/dev/mtdblock3 init=/linuxrc"
这里ttySAC0代表的是UART0字符设备/dev/ttySAC0,并不是随便指定的,是要根据实际UART驱动来指定。同样/dev/mtdblock3代表的是rootfs根文件系统所在的分区,也是根据实际块设备驱动来指定。
设置机器id为0xA8,如果u-boot MACH_TYPE_SMDK2440已经配置好了,这一步忽略:
SMDK2440 # set machid 0xA8 SMDK2440 # save Saving Environment to NAND... Erasing NAND... Erasing at 0x40000 -- 100% complete. Writing to NAND... OK SMDK2440 #
3.2 烧录内核
烧录内核有多种方法,下面我们就介绍两种常见的方法。
3.2.1 通过tftp烧录内核
我们搭建的tftp共享路径为/work/tftpboot。将uImage复制到该路径下:
root@zhengyang:/work# cp /work/sambashare/linux-5.2.8/arch/arm/boot/uImage /work/tftpboot/ root@zhengyang:/work# ll /work/tftpboot/ 总用量 3288 drwxrwxrwx 2 root root 4096 2月 8 20:22 ./ drwxr-xr-x 8 root root 4096 2月 10 17:10 ../ -rw-r--r-- 1 root root 3358328 2月 11 14:26 uImage
设置开发板ip地址,从而可以使用网络服务:
SMDK2440 # set ipaddr 192.168.0.105 SMDK2440 # save Saving Environment to NAND... Erasing NAND... Erasing at 0x40000 -- 100% complete. Writing to NAND... OK SMDK2440 # ping 192.168.0.200 dm9000 i/o: 0x20000000, id: 0x90000a46 DM9000: running in 16 bit mode MAC: 08:00:3e:26:0a:5b operating at unknown: 0 mode Using dm9000 device host 192.168.0.200 is alive
设置tftp服务器地址,也就是我们ubuntu服务器地址:
set serverip 192.168.0.200
save
下载内核到内存,并写NAND FLASH:
tftp 30000000 uImage nand erase.part kernel nand write 30000000 kernel
运行结果如下:
其中nand擦除命令可以替换成:
nand erase 0x60000 0x400000
地址设置为0x60000,大小为0x400000。是因为我的u-boot分区配置为:
mtdparts: mtdparts=Mini2440-0:256k(u-boot),128k(params),4m(kernel),-(rootfs);
可知256k+128k=400k,对应起始地址0x0060000。
3.2.2 通过MiniTools烧录内核
除了tftp烧录内核外,我们可以通过MiniTools工具烧录内核。
3.3 内核启动异常处理
开发板采用NAND启动,u-boot引导内核启动,可能会出现一些错误,针对一些常见错误处理如下。
3.3.1 machid设置错误
如果linux内核机器id设置不正确,串口将会输出:
U-Boot 2016.05 (Jan 22 2022 - 18:13:49 +0800) CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz DRAM: 64 MiB WARNING: Caches not enabled Flash: 0 Bytes NAND: 256 MiB *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: dm9000 Warning: dm9000 MAC addresses don't match: Address in SROM is ff:ff:ff:ff:ff:ff Address in environment is 08:00:3e:26:0a:5b Hit any key to stop autoboot: 5 4 3 2 1 0 NAND read: device 0 offset 0x60000, size 0x400000 4194304 bytes read: OK ## Booting kernel from Legacy Image at 30000000 ... Image Name: Linux-5.2.8 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 3581192 Bytes = 3.4 MiB Load Address: 30108000 Entry Point: 30108000 Verifying Checksum ... OK Loading Kernel Image ... OK Starting kernel ... Error: invalid dtb and unrecognized/unsupported machine ID r1=0x00000000, r2=0x00000000 Available machine support: ID (hex) NAME 00000400 AML_M5900 0000014b Simtec-BAST 0000015b IPAQ-H1940 0000039f Acer-N35 00000290 Acer-N30 000002a8 Nex Vision - Otom 1.1 00000454 QT2410 000000c1 SMDK2410 000005b4 TCT_HAMMER 000001db Thorcom-VR1000 000005d2 JIVE 000003fe SMDK2413 000003f1 SMDK2412 00000377 S3C2413 00000474 VSTMS 00000695 SMDK2416 000002de Simtec-Anubis 00000707 AT2440EVB 000007cf MINI2440 000002a9 NexVision - Nexcoder 2440 0000034a Simtec-OSIRIS 00000250 IPAQ-RX3715 0000016a SMDK2440 00000518 GTA02 000003b8 HP iPAQ RX1950 0000043c SMDK2443 Please check your kernel config and/or bootloader.
针对这种问题,修改machid即可。
3.3.2 NAND FLASH虚假坏块
如果开发板启动后出现如下错误:
U-Boot 2016.05 (Jan 25 2022 - 20:56:08 +0800) CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz DRAM: 64 MiB WARNING: Caches not enabled Flash: 0 Bytes NAND: 256 MiB In: serial Out: serial Err: serial Net: dm9000 Warning: dm9000 MAC addresses don't match: Address in SROM is ff:ff:ff:ff:ff:ff Address in environment is 08:00:3e:26:0a:5b Hit any key to stop autoboot: 5 4 3 2 1 0... Bad eraseblock 43 at 0x000000560000 Bad eraseblock 44 at 0x000000580000 Bad eraseblock 45 at 0x0000005a0000 Bad eraseblock 46 at 0x0000005c0000 Bad eraseblock 47 at 0x0000005e0000 Bad eraseblock 48 at 0x000000600000 Bad eraseblock 49 at 0x000000620000 Bad eraseblock 50 at 0x000000640000 Bad eraseblock 51 at 0x000000660000 Bad eraseblock 52 at 0x000000680000 Bad eraseblock 53 at 0x0000006a0000 Bad eraseblock 54 at 0x0000006c0000 Bad eraseblock 55 at 0x0000006e0000 Bad eraseblock 56 at 0x000000700000 Bad eraseblock 57 at 0x000000720000 Bad eraseblock 58 at 0x000000740000 Bad eraseblock 59 at 0x000000760000 Bad eraseblock 60 at 0x000000780000 Bad eraseblock 61 at 0x0000007a0000 Bad eraseblock 62 at 0x0000007c0000 Bad eraseblock 63 at 0x0000007e0000 Bad eraseblock 64 at 0x000000800000 Bad eraseblock 65 at 0x000000820000 Bad eraseblock 66 at 0x000000840000 Bad eraseblock 67 at 0x000000860000 Bad eraseblock 68 at 0x000000880000 Bad eraseblock 69 at 0x0000008a0000 Bad eraseblock 70 at 0x0000008c0000 Bad eraseblock 71 at 0x0000008e0000 Bad eraseblock 72 at 0x000000900000 Bad eraseblock 73 at 0x000000920000 Bad eraseblock 74 at 0x000000940000 Bad eraseblock 75 at 0x000000960000 ...
我们发现NAND FLASH坏块数目异常,而且都是连着坏的,并且使用nand dump命令查看flash内容,非常有规律,很可能是NAND FLASH很多块区域被标记为坏块了。
解决方法:在u-boot菜单模式下输入以下命令,擦除全片即可:
nand scrub.chip
3.4 正常启动
U-Boot 2016.05 (Jan 27 2022 - 23:02:32 +0800) CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz DRAM: 64 MiB WARNING: Caches not enabled Flash: 0 Bytes NAND: 256 MiB *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: dm9000 Warning: dm9000 MAC addresses don't match: Address in SROM is ff:ff:ff:ff:ff:ff Address in environment is 08:00:3e:26:0a:5b Hit any key to stop autoboot: 5 4 3 2 1 0 NAND read: device 0 offset 0x60000, size 0x400000 4194304 bytes read: OK ## Booting kernel from Legacy Image at 30000000 ... Image Name: Linux-5.2.8 Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 3656464 Bytes = 3.5 MiB Load Address: 30108000 Entry Point: 30108000 Verifying Checksum ... OK Loading Kernel Image ... OK Starting kernel ... Booting Linux on physical CPU 0x0 Linux version 5.2.8 (root@zhengyang) (gcc version 4.6.4 (crosstool-NG hg+unknown-20130521.154019 - tc0002)) #8 Thu Jan 27 23:05:27 CST 2022 CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f CPU: VIVT data cache, VIVT instruction cache Machine: SMDK2440 Memory policy: Data cache writeback CPU S3C2440A (id 0x32440001) Built 1 zonelists, mobility grouping on. Total pages: 16256 Kernel command line: root=/dev/mtdblock3 console=ttySAC0,115200 init=/linuxrc Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) Memory: 57232K/65536K available (5672K kernel code, 271K rwdata, 1260K rodata, 236K init, 196K bss, 8304K reserved, 0K cma-reserved) NR_IRQS: 111 S3C2440: IRQ Support irq: clearing pending status 00000002 random: get_random_bytes called from start_kernel+0x250/0x418 with crng_init=0 sched_clock: 16 bits at 1000kHz, resolution 1000ns, wraps every 32767500ns clocksource: samsung_clocksource_timer: mask: 0xffff max_cycles: 0xffff, max_idle_ns: 29163075 ns Console: colour dummy device 80x30 Calibrating delay loop... 199.06 BogoMIPS (lpj=995328) pid_max: default: 32768 minimum: 301 Mount-cache hash table entries: 1024 (order: 0, 4096 bytes) Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes) CPU: Testing write buffer coherency: ok Setting up static identity map for 0x30108400 - 0x3010847c clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns futex hash table entries: 256 (order: -1, 3072 bytes) NET: Registered protocol family 16 DMA: preallocated 256 KiB pool for atomic coherent allocations S3C Power Management, Copyright 2004 Simtec Electronics S3C2440: Initialising architecture SCSI subsystem initialized usbcore: registered new interface driver usbfs usbcore: registered new interface driver hub usbcore: registered new device driver usb s3c-i2c s3c2440-i2c.0: slave address 0x10 s3c-i2c s3c2440-i2c.0: bus frequency set to 97 KHz s3c-i2c s3c2440-i2c.0: i2c-0: S3C I2C adapter Advanced Linux Sound Architecture Driver Initialized. clocksource: Switched to clocksource samsung_clocksource_timer NET: Registered protocol family 2 tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes) TCP established hash table entries: 1024 (order: 0, 4096 bytes) TCP bind hash table entries: 1024 (order: 0, 4096 bytes) TCP: Hash tables configured (established 1024 bind 1024) UDP hash table entries: 256 (order: 0, 4096 bytes) UDP-Lite hash table entries: 256 (order: 0, 4096 bytes) NET: Registered protocol family 1 RPC: Registered named UNIX socket transport module. RPC: Registered udp transport module. RPC: Registered tcp transport module. RPC: Registered tcp NFSv4.1 backchannel transport module. NetWinder Floating Point Emulator V0.97 (extended precision) Initialise system trusted keyrings workingset: timestamp_bits=30 max_order=14 bucket_order=0 jffs2: version 2.2. (NAND) (SUMMARY) 漏 2001-2006 Red Hat, Inc. romfs: ROMFS MTD (C) 2007 Red Hat, Inc. Key type asymmetric registered Asymmetric key parser 'x509' registered io scheduler mq-deadline registered io scheduler kyber registered Console: switching to colour frame buffer device 30x40 s3c2410-lcd s3c2410-lcd: fb0: s3c2410fb frame buffer device Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled s3c2440-uart.0: ttySAC0 at MMIO 0x50000000 (irq = 74, base_baud = 0) is a S3C2440 printk: console [ttySAC0] enabled s3c2440-uart.1: ttySAC1 at MMIO 0x50004000 (irq = 77, base_baud = 0) is a S3C2440 s3c2440-uart.2: ttySAC2 at MMIO 0x50008000 (irq = 80, base_baud = 0) is a S3C2440 lp: driver loaded but no devices found ppdev: user-space parallel port driver brd: module loaded loop: module loaded nand: device found, Manufacturer ID: 0xec, Chip ID: 0xda nand: Samsung NAND 256MiB 3,3V 8-bit nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64 s3c24xx-nand s3c2440-nand: ECC disabled nand: NAND_ECC_NONE selected by board driver. This is not recommended! Scanning device for bad blocks Bad eraseblock 284 at 0x000002380000 Bad eraseblock 792 at 0x000006300000 Creating 4 MTD partitions on "NAND": 0x000000000000-0x000000040000 : "u-boot" 0x000000040000-0x000000060000 : "params" 0x000000060000-0x000000460000 : "kernel" 0x000000460000-0x000010000000 : "rootfs" s3c24xx-nand s3c2440-nand: Tacls=2, 20ns Twrph0=6 60ns, Twrph1=2 20ns ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver ohci-s3c2410: OHCI S3C2410 driver s3c2410-ohci s3c2410-ohci: OHCI Host Controller s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1 s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000 hub 1-0:1.0: USB hub found hub 1-0:1.0: 2 ports detected usbcore: registered new interface driver usbserial_generic usbserial: USB Serial support registered for generic usbcore: registered new interface driver ftdi_sio usbserial: USB Serial support registered for FTDI USB Serial Device usbcore: registered new interface driver pl2303 usbserial: USB Serial support registered for pl2303 s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq disabled NET: Registered protocol family 10 Segment Routing with IPv6 sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver NET: Registered protocol family 17 Loading compiled-in X.509 certificates hctosys: unable to open rtc device (rtc0) ALSA device list: No soundcards found. yaffs: dev is 32505859 name is "mtdblock3" rw yaffs: passed flags "" VFS: Mounted root (yaffs filesystem) on device 31:3. Freeing unused kernel memory: 236K This architecture does not have kernel memory protection. Run /linuxrc as init process Kernel panic - not syncing: Requested init /linuxrc failed (error -2). CPU: 0 PID: 1 Comm: swapper Not tainted 5.2.8 #8 Hardware name: SMDK2440 Backtrace: [<c011948c>] (dump_backtrace) from [<c01198d4>] (show_stack+0x18/0x24) r6:00000000 r5:00000000 r4:c084e3f0 r3:443347c2 [<c01198bc>] (show_stack) from [<c066e340>] (dump_stack+0x20/0x30) [<c066e320>] (dump_stack) from [<c0128bc4>] (panic+0xdc/0x2e4) [<c0128aec>] (panic) from [<c068d3a0>] (kernel_init+0x8c/0xfc) r3:c084de20 r2:fffffffe r1:c3ffcd10 r0:c073edf8 r7:00000000 [<c068d314>] (kernel_init) from [<c01090e0>] (ret_from_fork+0x14/0x34) Exception stack(0xc3821fb0 to 0xc3821ff8) 1fa0: 00000000 00000000 00000000 00000000 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000 r4:00000000 r3:ffffffff ---[ end Kernel panic - not syncing: Requested init /linuxrc failed (error -2). ]---
如果输出信息如上所示,表明内核启动正常,在下一节我们将介绍根文件系统的制作。
四、内核裁切
由于我们默认配置编译生成的内核文件是比较大,大概有3.5M的样子,这其中我们编译了许多无用的配置。
4.1 单板裁切
比如,默认编译的的内核,支持了多种单板。
执行make menuconfig,去除多余的单板:
System Type --->
- SAMSUNG S3C24XX SoCs Support --->
保留如下配置:
如上图所示,SoC下只选择S3C2440,单板文件下只选择SMDK2440相关。
4.2 文件系统裁切
重新make menuconfig,进入File systems,去掉:
- [] Second extended fs support
- [] The Extended 3 (ext3) filesystem
- [] The Extended 4 (ext4) filesystem
- CD-ROM/DVD Filesystems
- [] ISO 9660 CDROM file system support
更多裁切可以参考S3C2440移植linux3.4.2内核之内核裁剪。
裁切完之后,记得保存配置:
mv s3c2440_defconfig ./arch/arm/configs/
五、代码下载
Young / s3c2440_project[u-boot-2016.05-linux、linux-5.2.8]
参考文章
[4]S3C2440移植linux3.4.2内核之内核框架介绍及简单修改
[5]u-boot-2016.03 在mini2440移植之nandflash读写
[6]TQ2440(S3C2440)移植Linux-4.0.1内核全过程