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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM