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

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_config
make ARCH=arm CROSS_COMPILE=arm-linux- V=1

需要注意编译u-boot使用的是arm-linux-gcc4.3.2不要使用高版本,高版本编译出来的u-boot可能运行不了。

1.6 修改分区参数

中我们曾经设置分区参数如下:

/* 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)," \
                            "5m(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 ARCH=arm CROSS_COMPILE=arm-linux- V=1

下载u-boot.bin、zImage_P35到NAND FLASH运行,发现已经开始解压内核了,虽然还有其他错误,但是我们可以先忽略。

该图是我设置kernel为4m时截的图。

这里在将内核从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 0x500000
NAND read from offset 60000 failed -74
 0 bytes read: ERROR

发现出现同样的错误。我们参考中的分析。下面进行具体分析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;
}

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

还记的在中我们介绍过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-

2.3 内核mini2440_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

在linux内核根目录下执行:

make s3c2410_defconfig 

然后make menuconfig 进行微调:

make menuconfig

保存Save,输入文件名: 

在当前路径下生成s3c2440_defconfig:

存档:

mv s3c2440_defconfig ./arch/arm/configs/

2.4 修改时钟频率

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 修改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    = 0x00500000,
    },
    [3] = {
        .name    = "rootfs",
        .offset = MTDPART_OFS_APPEND,
        .size    = MTDPART_SIZ_FULL,
    }
};

256MB大小的NAND被分成四个分区:

  • 0x00000000~0x00040000:256kb存放u-boot;
  • 0x00040000~0x00060000: 128kb存放环境变量;
  • 0x00060000~0x00560000:5MB存放linux内核;
  • 0x00560000~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.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

然后 make uImage 再次编译内核,编译成功,在 arch/arm/boot 目录下生成 uImage

2.7 新版本交叉编译环境安装(内核编译失败安装)

我之前使用的为4.3.2版本,在编译高版本linux时出现错误,这里我将会将交叉编译环境升级到4.6.4:

下载地址:https://pan.baidu.com/s/1xuh8M8bQHfZt_w6h4vRKeg  提取码:uk85。

解压:

tar -jxvpf  arm-linux-gcc-4.6.4-arm-x86_64.tar.bz2

将其拷贝到/usr/local/arm路径下:

mv opt/TuxamitoSoftToolchains/arm-arm1176jzfssf-linux-gnueabi/gcc-4.6.4 /usr/local/arm

这样在/usr/local/arm下就有我们安装的两个不同版本的编译器:

接下来配置系统环境变量,把交叉编译工具链的路径添加到环境变量PATH中去,这样就可以在任何目录下使用这些工具:

编辑profile文件,添加环境变量:

vim /etc/profile

添加如下代码:

export PATH=$PATH:/usr/local/arm/gcc-4.6.4/bin

同时注释掉4.3.2版本的配置:

接下来使用命令:source /etc/profile,使修改后的profile文件生效。

然后,使用命令:arm-linux-gcc  -v查看当前交叉编译链工具的版本信息:

需要注意编译u-boot使用的是arm-linux-gcc4.3.2不要使用安装的这个高版本,高版本编译出来的u-boot运行不了。

三、烧录内核

如果我们需要修改linux内核代码重新编译,直接运行如下命令即可:

make clean
make distclean
make s3c2440_defconfig 
make V=1 uImage

注意:记得编译linux内核时将arm-linux-gcc设置为4.6.4版本。

如果我们需要修改u-boot代码重新编译,直接运行如下命令即可:

make clean
make distclean
make smdk2440_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux- V=1

注意:记得编译u-boot时将arm-linux-gcc设置为4.3.2版本。

修改完之后,编译内核。

3.1 内核启动前准备

首先将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 # 

如何确定linux内核机器id呢?在linux-5.2.8/arch/arm/mach-s3c24xx/mach-smdk2440.c中,struct machine_desc定义如下:

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks  */
        .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

显然,MINI2440使用的机器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查看。

3.2 通过tftp烧录内核

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 

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:    3581256 Bytes = 3.4 MiB
   Load Address: 30108000
   Entry Point:  30108000
   Verifying Checksum ... OK
   Loading Kernel Image ... OK
Using machid 0x16a from environment

Starting kernel ...

Warning: Neither atags nor dtb found
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)) #1 Tue Jan 25 23:00:07 CST 2022
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f
CPU: VIVT data cache, VIVT instruction cache
Machine: SMDK2440
Warning: Neither atags nor dtb found
Memory policy: Data cache writeback
CPU S3C2440A (id 0x32440001)
Built 1 zonelists, mobility grouping off.  Total pages: 4064
Kernel command line: root=/dev/hda1 ro init=/bin/bash console=ttySAC0
Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 8776K/16384K available (5427K kernel code, 267K rwdata, 1240K rodata, 228K init, 197K bss, 7608K reserved, 0K cma-reserved)
NR_IRQS: 111
S3C2440: IRQ Support
irq: clearing pending status 00000002
random: get_random_bytes called from start_kernel+0x250/0x414 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=12 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: soft ECC
Scanning device for bad blocks
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
Bad eraseblock 76 at 0x000000980000
Bad eraseblock 77 at 0x0000009a0000
Bad eraseblock 78 at 0x0000009c0000
Bad eraseblock 79 at 0x0000009e0000
Bad eraseblock 80 at 0x000000a00000
Bad eraseblock 81 at 0x000000a20000
Bad eraseblock 82 at 0x000000a40000
Bad eraseblock 83 at 0x000000a60000
Bad eraseblock 84 at 0x000000a80000
Bad eraseblock 85 at 0x000000aa0000
Bad eraseblock 86 at 0x000000ac0000
Bad eraseblock 87 at 0x000000ae0000
Bad eraseblock 88 at 0x000000b00000
Bad eraseblock 89 at 0x000000b20000
Bad eraseblock 90 at 0x000000b40000
Bad eraseblock 91 at 0x000000b60000
Bad eraseblock 92 at 0x000000b80000
Bad eraseblock 93 at 0x000000ba0000
Bad eraseblock 94 at 0x000000bc0000
Bad eraseblock 95 at 0x000000be0000
Bad eraseblock 96 at 0x000000c00000
Bad eraseblock 97 at 0x000000c20000
Bad eraseblock 98 at 0x000000c40000
Bad eraseblock 99 at 0x000000c60000
Bad eraseblock 100 at 0x000000c80000
Bad eraseblock 101 at 0x000000ca0000
Bad eraseblock 102 at 0x000000cc0000
Bad eraseblock 103 at 0x000000ce0000
Bad eraseblock 104 at 0x000000d00000
Bad eraseblock 105 at 0x000000d20000
Bad eraseblock 106 at 0x000000d40000
Bad eraseblock 107 at 0x000000d60000
Bad eraseblock 108 at 0x000000d80000
Bad eraseblock 109 at 0x000000da0000
Bad eraseblock 110 at 0x000000dc0000
Bad eraseblock 111 at 0x000000de0000
Bad eraseblock 284 at 0x000002380000
Bad eraseblock 792 at 0x000006300000
...

我们发现NAND FLASH坏块数目异常,而且都是连着坏的,并且使用nand dump命令查看flash内容,非常有规律,很可能是NAND FLASH很多块区域被标记为坏块了。

解决方法:在u-boot菜单模式下输入以下命令,擦除全片即可:

nand scrub.chip

3.6 正常启动

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
*** 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  0 
SMDK2440 # bdinfo
arch_number = 0x00000000
boot_params = 0x00000000
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
SMDK2440 # 
SMDK2440 # set machid 0xA8
SMDK2440 # save
Saving Environment to NAND...
Erasing NAND...

Erasing at 0x40000 -- 100% complete.
Writing to NAND... OK
SMDK2440 # print
baudrate=115200
bootargs=root=/dev/mtdblock3 console=ttySAC0,115200 rootfstype=yaffs2 init=/linuxrc
bootcmd=nand read 0x30000000 kernel; bootm 0x30000000
bootdelay=5
ethact=dm9000
ethaddr=08:00:3e:26:0a:5b
ipaddr=192.168.0.188
machid=0xA8
mtddevname=u-boot
mtddevnum=0
mtdids=nand0=Mini2440-0
mtdparts=mtdparts=Mini2440-0:256k(u-boot),128k(params),4m(kernel),-(rootfs);
netmask=255.255.255.0
partition=nand0,0
serverip=192.168.0.200
stderr=serial
stdin=serial
stdout=serial

Environment size: 511/131068 bytes
SMDK2440 # set bootargs "console=ttySAC0,115200 root=/dev/mtdblock3 init=/linuxrc"
SMDK2440 # save
Saving Environment to NAND...
Erasing NAND...

Erasing at 0x40000 -- 100% complete.
Writing to NAND... OK
SMDK2440 # 

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 

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:    3581256 Bytes = 3.4 MiB
   Load Address: 30108000
   Entry Point:  30108000
   Verifying Checksum ... OK
   Loading Kernel Image ... OK
Using machid 0xa8 from environment

Starting kernel ...

Warning: Neither atags nor dtb found
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)) #2 Tue Jan 25 23:37:44 CST 2022
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f
CPU: VIVT data cache, VIVT instruction cache
Machine: SMDK2440
Warning: Neither atags nor dtb found
Memory policy: Data cache writeback
CPU S3C2440A (id 0x32440001)
Built 1 zonelists, mobility grouping off.  Total pages: 4064
Kernel command line: root=/dev/hda1 ro init=/bin/bash console=ttySAC0
Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 8776K/16384K available (5427K kernel code, 267K rwdata, 1240K rodata, 228K init, 197K bss, 7608K reserved, 0K cma-reserved)
NR_IRQS: 111
S3C2440: IRQ Support
irq: clearing pending status 00000002
random: get_random_bytes called from start_kernel+0x250/0x414 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=12 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: soft ECC
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.
VFS: Cannot open root device "hda1" or unknown-block(0,0): error -6
Please append a correct "root=" boot option; here are the available partitions:
0100            4096 ram0 
 (driver?)
0101            4096 ram1 
 (driver?)
0102            4096 ram2 
 (driver?)
0103            4096 ram3 
 (driver?)
0104            4096 ram4 
 (driver?)
0105            4096 ram5 
 (driver?)
0106            4096 ram6 
 (driver?)
0107            4096 ram7 
 (driver?)
0108            4096 ram8 
 (driver?)
0109            4096 ram9 
 (driver?)
010a            4096 ram10 
 (driver?)
010b            4096 ram11 
 (driver?)
010c            4096 ram12 
 (driver?)
010d            4096 ram13 
 (driver?)
010e            4096 ram14 
 (driver?)
010f            4096 ram15 
 (driver?)
1f00             256 mtdblock0              // 分区u-boot
 (driver?)
1f01             128 mtdblock1              // 分区params
 (driver?)
1f02            4096 mtdblock2              // 分区kernel
 (driver?)
1f03          257664 mtdblock3              // 分区rootfs 
 (driver?)
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
CPU: 0 PID: 1 Comm: swapper Not tainted 5.2.8 #2
Hardware name: SMDK2440
Backtrace: 
[] (dump_backtrace) from [] (show_stack+0x18/0x1c)
 r6:c0918000 r5:00000000 r4:c0809434
[] (show_stack) from [] (dump_stack+0x20/0x28)
[] (dump_stack) from [] (panic+0xdc/0x2d4)
[] (panic) from [] (mount_block_root+0x1a0/0x2a0)
 r3:07340abb r2:07340abb r1:c0f99ec8 r0:c07004ac
 r7:c0fe6300
[] (mount_block_root) from [] (mount_root+0x108/0x134)
 r10:c078d608 r9:c07c4a5c r8:000000b2 r7:c0808e80 r6:c07b7830 r5:c07b7869
 r4:00000000
[] (mount_root) from [] (prepare_namespace+0x168/0x1bc)
 r7:c0808e80 r6:c07b7830 r5:c07b7869 r4:c0808eac
[] (prepare_namespace) from [] (kernel_init_freeable+0x17c/0x1c0)
 r5:c07b7850 r4:00000007
[] (kernel_init_freeable) from [] (kernel_init+0x10/0xf4)
 r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0650054
 r4:00000000
[] (kernel_init) from [] (ret_from_fork+0x14/0x34)
Exception stack(0xc0f99fb0 to 0xc0f99ff8)
9fa0:                                     00000000 00000000 00000000 00000000
9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
 r4:00000000
---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

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

四、代码下载

Young / s3c2440_project[u-boot-2016.05-linux、linux-5.2.8]

参考文章

[1]

[2]

[3]s3c2440 linux3.4.2移植笔记

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

[5]

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

[7]移植linux-5.4.26到jz2440

[8][7]

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