程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

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

在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 修改时钟频率

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

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

四、内核裁切

由于我们默认配置编译生成的内核文件是比较大,大概有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]

参考文章

[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上的移植

posted @ 2022-01-19 22:43  大奥特曼打小怪兽  阅读(1773)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步