• 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 启动参数配置

    在smdk2440.h(include/configs/smdk2440.h)文件中配置启动参数:

    #define CONFIG_BOOTARGS   "root=/dev/mtdblock3 console=ttySAC0,115200  init=/linuxrc"
    • root:指定文件系统位置这里配置为NAND三号分区,也就是我们根文件系统所在的分区;
    • init:指定内核启动后执行的第一个应用程序;
    • console:指定使用哪个终端,这里的 ttySAC0 指的就是串口0;

    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.3 启动命令配置

    在smdk2440.h文件中配置启动命令:

    #define CONFIG_BOOTCOMMAND "nand read 0x30000000 kernel; bootm 0x30000000" //bootcmd  

    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.5 编译下载

    重新编译,下载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.6 修改分区参数

    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、内核:

    然后运行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.7 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;
    }
    View Code

    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;
    }
    View Code

    还记的在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.8 测试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/

    我们这里下载linuxz-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 文件,找到下面语句:

    ARCH        ?= $(SUBARCH)

    修改为:

    ARCH        ?= arm
    CROSS_COMPILE    ?= arm-linux-

    其中,ARCH 是指定目标平台为arm,CROSS_COMPILE是指定交叉编译器,这里指定的 是系统默认的交叉编译器,如要使用其它的,则要把编译器的全路径在这里写出。

    2.4 内核s3c2440_defconfig配置

    接下来要做的就是内核配置、编译了。单板的默认配置文件在 arch/arm/configs 目录下,如果没有2440相关的默认配置,可以选择比较相近的 2410 的配置修改,所以我这里直接选择s3c2410_defconfig。

    配置文件s3c2410_defconfig支持很多单板,包括2440、2410。

    # CONFIG_LOCALVERSION_AUTO is not set
    CONFIG_SYSVIPC=y
    CONFIG_POSIX_MQUEUE=y
    CONFIG_RELAY=y
    CONFIG_BLK_DEV_INITRD=y
    # CONFIG_COMPAT_BRK is not set
    CONFIG_MODULES=y
    CONFIG_MODULE_FORCE_LOAD=y
    CONFIG_MODULE_UNLOAD=y
    CONFIG_MODULE_FORCE_UNLOAD=y
    # CONFIG_BLK_DEV_BSG is not set
    CONFIG_BLK_DEV_INTEGRITY=y
    CONFIG_PARTITION_ADVANCED=y
    CONFIG_BSD_DISKLABEL=y
    CONFIG_MINIX_SUBPARTITION=y
    CONFIG_SOLARIS_X86_PARTITION=y
    CONFIG_UNIXWARE_DISKLABEL=y
    CONFIG_LDM_PARTITION=y
    CONFIG_ARCH_S3C24XX=y
    # CONFIG_CPU_S3C2410 is not set
    CONFIG_CPU_S3C2440=y
    CONFIG_MACH_MINI2440=y
    CONFIG_S3C_ADC=y
    CONFIG_S3C24XX_PWM=y
    CONFIG_AEABI=y
    CONFIG_KEXEC=y
    CONFIG_CPU_IDLE=y
    CONFIG_BINFMT_MISC=m
    CONFIG_APM_EMULATION=y
    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_ADVANCED_ROUTER=y
    CONFIG_IP_MULTIPLE_TABLES=y
    CONFIG_IP_ROUTE_MULTIPATH=y
    CONFIG_IP_ROUTE_VERBOSE=y
    CONFIG_IP_PNP=y
    CONFIG_IP_PNP_DHCP=y
    CONFIG_IP_PNP_BOOTP=y
    CONFIG_IP_PNP_RARP=y
    CONFIG_IP_MROUTE=y
    CONFIG_IP_PIMSM_V1=y
    CONFIG_IP_PIMSM_V2=y
    CONFIG_SYN_COOKIES=y
    # CONFIG_INET_XFRM_MODE_TRANSPORT is not set
    # CONFIG_INET_XFRM_MODE_TUNNEL is not set
    # CONFIG_INET_XFRM_MODE_BEET is not set
    CONFIG_INET_DIAG=m
    # CONFIG_IPV6 is not set
    CONFIG_NETFILTER=y
    CONFIG_BRIDGE=m
    CONFIG_VLAN_8021Q=m
    CONFIG_VLAN_8021Q_GVRP=y
    CONFIG_NET_PKTGEN=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_HCIBTUSB=m
    CONFIG_BT_HCIBTSDIO=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_CONNECTOR=m
    CONFIG_MTD=y
    CONFIG_MTD_CMDLINE_PARTS=y
    CONFIG_MTD_BLOCK=y
    CONFIG_FTL=y
    CONFIG_NFTL=y
    CONFIG_NFTL_RW=y
    CONFIG_INFTL=y
    CONFIG_RFD_FTL=y
    CONFIG_MTD_CFI=y
    CONFIG_MTD_JEDECPROBE=y
    CONFIG_MTD_CFI_AMDSTD=y
    CONFIG_MTD_CFI_STAA=y
    CONFIG_MTD_RAM=y
    CONFIG_MTD_ROM=y
    CONFIG_MTD_RAW_NAND=y
    CONFIG_MTD_NAND_S3C2410=y
    CONFIG_MTD_NAND_PLATFORM=y
    CONFIG_MTD_LPDDR=y
    CONFIG_BLK_DEV_LOOP=m
    CONFIG_BLK_DEV_NBD=m
    CONFIG_BLK_DEV_RAM=y
    CONFIG_BLK_DEV_RAM_SIZE=65536
    CONFIG_CDROM_PKTCDVD=m
    CONFIG_SENSORS_TSL2550=m
    CONFIG_EEPROM_AT24=y
    CONFIG_SCSI=m
    # CONFIG_SCSI_PROC_FS is not set
    CONFIG_BLK_DEV_SD=m
    CONFIG_CHR_DEV_SG=m
    # CONFIG_SCSI_LOWLEVEL is not set
    CONFIG_NETDEVICES=y
    CONFIG_TUN=m
    CONFIG_DM9000=y
    CONFIG_PPP=m
    CONFIG_PPP_BSDCOMP=m
    CONFIG_PPP_DEFLATE=m
    CONFIG_PPP_FILTER=y
    CONFIG_PPP_MPPE=m
    CONFIG_PPP_MULTILINK=y
    CONFIG_PPP_ASYNC=m
    CONFIG_PPP_SYNC_TTY=m
    CONFIG_HOSTAP=m
    CONFIG_HOSTAP_FIRMWARE=y
    CONFIG_HOSTAP_FIRMWARE_NVRAM=y
    CONFIG_LIBERTAS=m
    CONFIG_LIBERTAS_SDIO=m
    CONFIG_ZD1211RW=m
    CONFIG_ZD1211RW_DEBUG=y
    CONFIG_INPUT_FF_MEMLESS=y
    CONFIG_INPUT_EVDEV=y
    CONFIG_INPUT_EVBUG=m
    # CONFIG_KEYBOARD_ATKBD is not set
    CONFIG_KEYBOARD_GPIO=y
    CONFIG_INPUT_TOUCHSCREEN=y
    CONFIG_SERIO_RAW=y
    CONFIG_LEGACY_PTY_COUNT=128
    CONFIG_SERIAL_SAMSUNG=y
    CONFIG_SERIAL_SAMSUNG_CONSOLE=y
    CONFIG_SERIAL_DEV_BUS=m
    CONFIG_IPMI_HANDLER=m
    CONFIG_IPMI_DEVICE_INTERFACE=m
    CONFIG_IPMI_SI=m
    CONFIG_IPMI_WATCHDOG=m
    CONFIG_IPMI_POWEROFF=m
    CONFIG_HW_RANDOM=y
    CONFIG_I2C=y
    CONFIG_I2C_CHARDEV=y
    CONFIG_I2C_S3C2410=y
    CONFIG_I2C_SIMTEC=y
    CONFIG_SPI=y
    CONFIG_SPI_S3C24XX=y
    CONFIG_SPI_SPIDEV=y
    CONFIG_GPIO_SYSFS=y
    CONFIG_SENSORS_LM75=y
    CONFIG_THERMAL=y
    CONFIG_WATCHDOG=y
    CONFIG_S3C2410_WATCHDOG=y
    CONFIG_FB=y
    CONFIG_FIRMWARE_EDID=y
    CONFIG_FB_MODE_HELPERS=y
    CONFIG_FB_TILEBLITTING=y
    CONFIG_FB_S3C2410=y
    CONFIG_BACKLIGHT_LCD_SUPPORT=y
    CONFIG_LCD_CLASS_DEVICE=y
    CONFIG_LCD_PLATFORM=y
    CONFIG_BACKLIGHT_CLASS_DEVICE=y
    # CONFIG_BACKLIGHT_GENERIC is not set
    CONFIG_BACKLIGHT_PWM=y
    CONFIG_FRAMEBUFFER_CONSOLE=y
    CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
    CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
    CONFIG_LOGO=y
    # CONFIG_LOGO_LINUX_MONO is not set
    # CONFIG_LOGO_LINUX_VGA16 is not set
    CONFIG_SOUND=y
    CONFIG_SND=y
    CONFIG_SND_SEQUENCER=m
    CONFIG_SND_SEQ_DUMMY=m
    CONFIG_SND_MIXER_OSS=m
    CONFIG_SND_PCM_OSS=m
    CONFIG_SND_SEQUENCER_OSS=y
    CONFIG_SND_DYNAMIC_MINORS=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_USB_CAIAQ_INPUT=y
    CONFIG_SND_SOC=y
    CONFIG_HIDRAW=y
    CONFIG_HID_GYRATION=y
    CONFIG_HID_NTRIG=y
    CONFIG_HID_PANTHERLORD=y
    CONFIG_HID_PETALYNX=y
    CONFIG_HID_SAMSUNG=y
    CONFIG_HID_SONY=y
    CONFIG_HID_SUNPLUS=y
    CONFIG_HID_TOPSEED=y
    CONFIG_HID_PID=y
    CONFIG_USB_HIDDEV=y
    CONFIG_USB=y
    CONFIG_USB_OHCI_HCD=y
    CONFIG_USB_ACM=m
    CONFIG_USB_WDM=m
    CONFIG_USB_STORAGE=m
    CONFIG_USB_STORAGE_DATAFAB=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_SERIAL=m
    CONFIG_USB_SERIAL_CP210X=m
    CONFIG_USB_SERIAL_FTDI_SIO=m
    CONFIG_USB_SERIAL_SPCP8X5=m
    CONFIG_USB_GADGET=y
    CONFIG_USB_S3C2410=y
    CONFIG_USB_ZERO=m
    CONFIG_USB_ETH=m
    CONFIG_USB_GADGETFS=m
    CONFIG_USB_MASS_STORAGE=m
    CONFIG_USB_G_SERIAL=m
    CONFIG_USB_CDC_COMPOSITE=m
    CONFIG_MMC=y
    CONFIG_SDIO_UART=y
    CONFIG_MMC_SDHCI=y
    CONFIG_MMC_SPI=y
    CONFIG_MMC_S3C=y
    CONFIG_LEDS_S3C24XX=y
    CONFIG_LEDS_GPIO=y
    CONFIG_LEDS_TRIGGER_TIMER=y
    CONFIG_LEDS_TRIGGER_HEARTBEAT=y
    CONFIG_LEDS_TRIGGER_GPIO=y
    CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
    CONFIG_RTC_CLASS=y
    CONFIG_RTC_INTF_DEV_UIE_EMUL=y
    CONFIG_RTC_DRV_S3C=y
    CONFIG_DMADEVICES=y
    CONFIG_S3C24XX_DMAC=y
    CONFIG_EXT2_FS=m
    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_EXT3_FS_SECURITY=y
    CONFIG_AUTOFS4_FS=y
    CONFIG_MSDOS_FS=y
    CONFIG_VFAT_FS=y
    CONFIG_TMPFS=y
    CONFIG_TMPFS_POSIX_ACL=y
    CONFIG_JFFS2_FS=y
    CONFIG_CRAMFS=y
    CONFIG_ROMFS_FS=y
    CONFIG_ROMFS_BACKED_BY_BOTH=y
    CONFIG_NFS_FS=y
    CONFIG_NFS_V3_ACL=y
    CONFIG_NFS_V4=y
    CONFIG_ROOT_NFS=y
    CONFIG_NLS_DEFAULT="cp437"
    CONFIG_NLS_CODEPAGE_437=m
    CONFIG_NLS_CODEPAGE_737=m
    CONFIG_NLS_CODEPAGE_775=m
    CONFIG_NLS_CODEPAGE_850=m
    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=m
    CONFIG_NLS_ISO8859_1=m
    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_ENABLE_MUST_CHECK is not set
    CONFIG_STRIP_ASM_SYMS=y
    CONFIG_DEBUG_FS=y
    CONFIG_DEBUG_KERNEL=y
    # CONFIG_SCHED_DEBUG is not set
    CONFIG_DEBUG_USER=y
    CONFIG_CRYPTO_CRYPTD=m
    CONFIG_CRYPTO_AUTHENC=m
    CONFIG_CRYPTO_TEST=m
    CONFIG_CRYPTO_CTS=m
    CONFIG_CRYPTO_ECB=y
    CONFIG_CRYPTO_LRW=m
    CONFIG_CRYPTO_PCBC=m
    CONFIG_CRYPTO_XTS=m
    CONFIG_CRYPTO_HMAC=y
    CONFIG_CRYPTO_XCBC=m
    CONFIG_CRYPTO_MD4=m
    CONFIG_CRYPTO_MICHAEL_MIC=y
    CONFIG_CRYPTO_RMD128=m
    CONFIG_CRYPTO_RMD160=m
    CONFIG_CRYPTO_RMD256=m
    CONFIG_CRYPTO_RMD320=m
    CONFIG_CRYPTO_SHA512=m
    CONFIG_CRYPTO_TGR192=m
    CONFIG_CRYPTO_WP512=m
    CONFIG_CRYPTO_ANUBIS=m
    CONFIG_CRYPTO_ARC4=y
    CONFIG_CRYPTO_BLOWFISH=m
    CONFIG_CRYPTO_CAMELLIA=m
    CONFIG_CRYPTO_CAST5=m
    CONFIG_CRYPTO_CAST6=m
    CONFIG_CRYPTO_FCRYPT=m
    CONFIG_CRYPTO_KHAZAD=m
    CONFIG_CRYPTO_SALSA20=m
    CONFIG_CRYPTO_SEED=m
    CONFIG_CRYPTO_SERPENT=m
    CONFIG_CRYPTO_TEA=m
    CONFIG_CRYPTO_TWOFISH=m
    CONFIG_CRYPTO_DEFLATE=m
    CONFIG_CRYPTO_LZO=m
    CONFIG_LIBCRC32C=m
    CONFIG_FONTS=y
    CONFIG_FONT_8x8=y
    CONFIG_FONT_MINI_4x6=y
    View Code

    在linux内核根目录下执行,生成默认配置文件.config:

    make s3c2410_defconfig 

    然后make menuconfig 修改配置:

    make menuconfig

    保存文件,输入文件名: 

    在当前路径下生成s3c2440_defconfig:

    存档:

    mv s3c2440_defconfig ./arch/arm/configs/

    存档之后,下次如果需要重新编译配置直接运行make s3c2440_defconfig,避免了重新进行menuconfig配置。

    在arch/arm/mach-s3c24xx目录下有个mach-smdk2440.c文件,这个文件是三星厂商提供的smdk2440开发版对应的示例程序。后面我们会对这个文件进行修改来适配Mini2440开发板。

    2.5 修改时钟频率

    s3c2440 支持两种时钟晶振:12MHz 和 16MHz,我这个板子上用的是12MHz,所以修改 arch/arm/mach-s3c24xx/mach-smdk2440.c:

    static void __init smdk2440_init_time(void)
    {
        s3c2440_init_clocks(12000000);
        samsung_timer_init();
    }

    2.6 修改mtd分区

    打开 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.7 关闭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_NOE,这个去掉ecc校验的问题,在内核中明确说明是不建议这样做的,因为这样就等于忽略了对NAND FLASH坏块的检测。

    2.8 设置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_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.9 编译内核制作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.10 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 操作系统的会用到。

    更多不同编译器之间的区别参考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.83/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查看当前交叉编译链工具的版本信息:

    三、烧录内核

    如果我们需要修改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"

    设置机器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 通过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.3 通过MiniTools烧录内核

    3.4 machid设置错误

    开发板采用NAND启动,启动后,如果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.5 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.6 正常启动

    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). ]---
    View Code

    如果输出信息如上所示,表明内核启动正常,在下一节我们将介绍根文件系统的制作。

    四、内核裁切

    由于我们默认配置编译生成的内核文件是比较大,大概有3.5M的样子,这其中我们编译了许多无用的配置。

    4.1 单板裁切

    比如,默认编译的的内核,支持了多种单板。

    执行make menuconfig,去除多余的单板:

    System Type --->

    • SAMSUNG S3C24XX SoCs Support --->

    保留如下配置:

    如上图所示,CPU下只选择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]

    参考文章

    [1]七,移植linux-3.19内核

    [2]S3C2440 移植最新5.2linux内核

    [3]s3c2440 linux3.4.2移植笔记

    [4]S3C2440移植linux3.4.2内核之内核框架介绍及简单修改

    [5]u-boot-2016.03 在mini2440移植之nandflash读写

    [6]TQ2440(S3C2440)移植Linux-4.0.1内核全过程

    [7]移植linux-5.4.26到jz2440

    [8]u-boot-2016.03 支持yaffs2文件系统烧写之添加nand write.yaffs2命令

    [9]linux4.9.2内核在mini2440上的移植

  • 相关阅读:
    VIJOS1476 旅行规划(树形Dp + DFS暴力乱搞)
    神奇的图片
    How to locate elements/ Object locators for Android devices
    ZT: How to install appium with node.js
    How to get the appPackage and appActivity
    How to enable auto-complete for python in powershell
    Node.js
    Steps to develop an iterative algorithm
    Iterative Algorithm
    FSM
  • 原文地址:https://www.cnblogs.com/zyly/p/15824029.html
Copyright © 2020-2023  润新知