1.移植3.4內核-分析內核啟動過程,重新分區,燒寫jffs2文件系統

来源:https://www.cnblogs.com/lifexy/archive/2018/01/23/8337453.html
-Advertisement-
Play Games

1.在上章-移植uboot里.我們來分析下uboot是如何進入到內核的 首先,uboot啟動內核是通過bootcmd命令行實現的,在我們之前移植的bootcmd命令行如下所示: 1.1然後我們進入cmd_bootm.c,找到對應的bootm命令對應的do_bootm(): 上面的boot_os_fn ...


1.在上章-移植uboot里.我們來分析下uboot是如何進入到內核的

首先,uboot啟動內核是通過bootcmd命令行實現的,在我們之前移植的bootcmd命令行如下所示:

bootcmd=nand read  0x30000000 kernel; bootm 0x30000000        //bootm:從0x30000000處啟動內核

1.1然後我們進入cmd_bootm.c,找到對應的bootm命令對應的do_bootm():

int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
boot_os_fn *boot_fn;             //boot_fn是個數組函數
 ... ..

boot_fn(0, argc, argv, &images); //調用數組函數
 ... ...
}

上面的boot_os_fn是個typedef型,如下圖所示:

 

 

1.2由於定義了巨集CONFIG_BOOTM_LINUX,最終會跳轉到do_bootm ->do_bootm_linux()

代碼如下所示:

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
         /* No need for those on ARM */
         if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
                   return -1;
         if (flag & BOOTM_STATE_OS_PREP) {
                   boot_prep_linux(images);
                   return 0;
         }
         if (flag & BOOTM_STATE_OS_GO) {
                   boot_jump_linux(images);
                   return 0;
         }

 
         boot_prep_linux(images);      //該函數會將各個tag參數保存在指定位置,比如:記憶體tag、bootargs環境變數tag、串口tag等
         boot_jump_linux(images);      //該函數會跳轉到內核起始地址
         return 0;
}

 

1.3最終跳轉到do_bootm ->do_bootm_linux-> boot_jump_linux()

代碼如下所示:

static void boot_jump_linux(bootm_headers_t *images)
{
         unsigned long machid = gd->bd->bi_arch_number;     //獲取機器ID
         char *s;
         void (*kernel_entry)(int zero, int arch, uint params);
         unsigned long r2;
         kernel_entry = (void (*)(int, int, uint))images->ep;  //設置kernel_entry()的地址為0x30000000
         s = getenv("machid");                     //判斷環境變數machid是否設置,若設置則使用環境變數里的值   
         if (s) {       
                   strict_strtoul(s, 16, &machid);      //重新獲取機器ID
                   printf("Using machid 0x%lx from environment\n", machid);  //使用環境變數的machid
         }
     ... ...
r2 = gd->bd->bi_boot_params; //獲取tag參數地址, gd->bd->bi_boot_params在setup_start_tag()函數里被設置 kernel_entry(0, machid, r2); //跳轉到0x30000000,r0=0,r1=機器ID,r2=tag參數地址 }

 上面的machid預設值為MACH_TYPE_SMDK2410(也就是193),我們也可以在環境變數里設置machid變數

 

1.4最終,便跳到內核執行代碼,步驟如下所示:

  • 1)根據R1(機器ID),來判斷內核是否支持該機器,若支持則初始化機器相關函數
  • 2)解析TAG參數,初始化串口,設置記憶體等
  • 3)掛載根文件系統,並執行應用程式

 

2.接下來便從網上下載3.4.2內核來移植.

2.1修改Makefile,修改配置

tar xjf linux-3.4.2.tar.bz2 
cd linux-3.4.2/
vi Makefile 

找到下麵這句話:

ARCH            ?= $(SUBARCH)
CROSS_COMPILE   ?= $(CONFIG_CROSS_COMPILE:"%"=%)

 改為:

ARCH            ?= arm
CROSS_COMPILE   ?= arm-linux-

 (PS:我使用的是4.3.2,若交叉編譯工具版本太低,可能無法編譯)

2.2 配置編譯

cd arch/arm/configs                //由於我們板子是arm板,進入該目錄
ls  *2440*                            //找到有mini2440_defconfig、
ls  *2410*                            //找到有s3c2410_defconfig
make s3c2410_defconfig                //配置2410, 更新.config配置文件 
make uImage                          //編譯,生成uImage
cp uImage /work/nfs_root/ uImage_new           //用nfs下載

 

3進入uboot燒寫

nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new
bootm 32000000  

如下圖所示,發現串口輸出亂碼:

 

出現這個問題,可以先看看bootargs命令行的串口設置是否正確、uboot傳遞的機器ID是否正確.

3.1找到bootargs命令行的串口沒有設置波特率,修改bootargs:

set bootargs root=/dev/mtdblock3 console=ttySAC0,115200 

 

3.2 測試機器ID是否正確

在我們1.3小節代碼分析里,講到過uboot傳遞進來的機器ID可以通過環境變數machid來設置

所以任意設置一個ID,這樣再次啟動內核時,內核識別不出來,就會列印出所有設備對應的機器ID

進入uboot,輸入:

set machid 33333
tftp 32000000 uImage
bootm 32000000

如下圖所示,由於內核不支持這個機器ID,所以列印出內核能支持的ID表:

 

由於我們板子是2440,所以測試7cf(mini2440)以及16a(smdk2440)這兩個機器ID,是否支持我們開發板

發現只有7cf(mini2440)這個ID,有串口輸出正常.

來看看16a(smdk2440)為什麼串口亂碼,進入mach-smdk2440.c( 位於arch/arm/mach-s3c24xx)

找到問題出在smdk2440_map_io():

static void __init smdk2440_map_io(void)
{
         s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
         s3c24xx_init_clocks(16934400);             //初始化時鐘clock
         s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}

由於我們板子上的晶振是12Mhz,而mdk2440_map_io()里,初始化的時鐘是基於16934400hz的晶振.

所以將:

s3c24xx_init_clocks(16934400);             //初始化時鐘clock

改為:

s3c24xx_init_clocks(12000000);             //初始化時鐘clock

然後重新編譯uImage:

make  s3c2410_defconfig             //將mach-s3c2440.c配置進內核
make  uImage
cp uImage /work/nfs_root/ uImage_new

進入uboot,輸入:

set machid 16a
nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new
boom 32000000

啟動內核,列印如下圖所示:

 

如上圖所示,內核創建了8個分區,而我們移植的uboot只有4個分區,代碼如下:

0x00000000-0x00040000 : "bootloader"            //存放uboot
0x00040000-0x00060000 : "params"                //存放環境變數
0x00060000-0x00260000 : "kernel"                //存放內核
0x00260000-0x10000000 : "rootfs"                  //存放文件系統

uboot傳遞的文件系統路徑root=/dev/mtdblock3,所以內核便卡死在啟動文件系統上

 

4.所以接下來我們來修改內核分區

4.1在si里搜索上圖出現的”S3C2410 flash partition”欄位

如下圖所示:

 

找到位於common-smdk.c中,裡面有個數組smdk_default_nand_part[],內容如下所示:

 

4.2接下來便修改smdk_default_nand_part[]數組(位於arch/arm/mach-s3c24xx/common-smdk.c)

修改為:

static struct mtd_partition smdk_default_nand_part[] = {
         [0] = {
                   .name         = "bootloader",            //0x00000000-0x00040000  
                   .size  = SZ_256K,                      
                   .offset         = 0,
         },

         [1] = {
                   .name         = "params",                 //0x00040000-0x00060000
                   .offset = MTDPART_OFS_APPEND, 
                   .size  = SZ_128K,
         },

         [2] = {
                   .name         = "kernel",                  //0x00060000-0x00260000
                   .offset = MTDPART_OFS_APPEND,
                   .size  = SZ_2M,
         },

         [3] = {
                   .name         = "rootfs",                 //0x00260000-0x10000000
                   .offset         = MTDPART_OFS_APPEND,
                   .size  = MTDPART_SIZ_FULL,
         }
};

上面部分巨集的定義,如下所示:

  • MTDPART_OFS_RETAIN:    填在offset里,表示先後保留多少size空間大小
  • MTDPART_OFS_NXTBLK:   填在offset里,表示從下一個塊開始
  • MTDPART_OFS_APPEND:  填在offset里,表示該分區位置附加在上個分區結束的地址上
  • MTDPART_SIZ_FULL:         填在size里,表示剩下的記憶體size都歸於該分區

4.3若需要mini2440的機器ID,則還需要修改mini2440單板對應的mach-mini2440.c

因為該單板的mtd分區也不對,將裡面的mini2440_default_nand_part[]內容改為和上面一樣

4.4改好後,重啟內核,發現內核還是啟動不了以前的yaffs文件系統

如下圖所示:

 

表示不支持該內核不支持yaffs文件系統,然後嘗試使用ext3 ext2 cramfs vfat msdos iso9660等來掛載

4.5 嘗試使用以前的jffs2文件系統

重新燒寫jffs2,設置uboot環境變數,啟動內核,列印如下圖:

 

上圖,表示jffs2已掛載,但是找不到init程式,因為這個文件系統的glibc庫是交叉編譯3.4版本的,由於3.4內核的交叉編譯是4.3版本,所以不支持,接下來我們便重新製作文件系統

 

5.構造根文件系統

5.1首先編譯安裝busybox(參考以前的busybox安裝章節)

進入https://busybox.net/oldnews.html下載busybox 1.20.0

tar -xjf busybox-1.20.0.tar.bz2
cd busybox-1.20.0
make menuconfig          //設置交叉編譯首碼

進入Busybox Settings --->Build Options --->

() Cross Compiler prefix

在彈出的對話框裡面寫入:arm-linux-

make          //編譯
mkdir   /work/nfs_root/fs_mini_mdev_new            //創建要安裝的文件系統目錄      
make install CONFIG_PREFIX=/work/nfs_root/fs_mini_mdev_new   //指定安裝位置

 

5.2 安裝glibc庫

輸入$PATH找到交叉編譯位於/work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2位置,

通過find -name lib,找到有以下幾個lib

 

由於ARM9屬於ARMv4T架構,所以拷貝上面兩條紅線處的lib到fs_mini_mdev_new里

mkdir /work/nfs_root/fs_mini_mdev_new/lib
mkdir /work/nfs_root/fs_mini_mdev_new/usr/lib -p  
cp arm-none-linux-gnueabi/libc/armv4t/usr/lib/*.so* /work/nfs_root/fs_mini_mdev_new/usr/lib -d           /* -d:保持鏈接 */
cp arm-none-linux-gnueabi/libc/armv4t/lib/*.so* /work/nfs_root/fs_mini_mdev_new/lib -d

 

5.3 構造etc目錄

在etc目錄下,需要構造以下3個文件

  • etc/inittab     : init進程會根據inittab文件里,來創建其它子進程,比如
  • etc/init.d/rcS:腳本文件,裡面用來執行命令,比如設置網卡,使用mount -a來裝載/etc/fstab中的文件系統
  • etc/fstab      :裡面保存要被掛載的哪個文件系統,比如proc、sysfs、tmpfs、devpts等系統

1)構造/etc/inittab

cd  cd /work/nfs_root/fs_mini_mdev_new/
mkdir etc/
vi etc/inittab

添加以下幾句:

::sysinit:/etc/init.d/rcS    //內核啟動時,執行/etc/init.d/rcS
console::askfirst:-/bin/sh  //啟動console對應的-/bin/sh進程之前,等待用戶按enter鍵
::ctrlaltdel:/sbin/reboot   //按下ctrl+alt+del組合鍵時,啟動reboot命令
::shutdown:/bin/umount -a -r  //系統關機前,卸載所有文件系統

2)構造etc/init.d/rcS

mkdir etc/init.d/   
vi  etc/init.d/rcS

添加以下幾句:

mount -a               //裝載/etc/fstab中的文件系統
echo /sbin/mdev > /proc/sys/kernel/hotplug  //使/sbin/medv指向hotplug,從而支持熱拔插
mdev -s                //使用medv命令自動創建/dev下的所有設備節點

3)構造etc/fstab

PS:使用mdev命令需要sysfs、tmpfs、devpts這3個文件系統的支持

mkdir proc/                            //創建proc要掛載的目錄
mkdir sys/                       //創建sysfs要掛載的目錄,
mkdir dev/pts -p              //創建devpts要掛載的目錄
vi  etc/fstab

添加以下幾句

# device     mount-point    type     options      dump  fsck order
proc           /proc        proc     defaults      0     0
tmpfs          /tmp         tmpfs    defaults      0     0
sysfs          /sys         sysfs    defaults      0     0
devpts         /dev/pts     devpts   defaults      0     0

 

5.4構造其它文件/目錄

1)創建終端文件(dev/console和dev/null)

sudo mknod –m 660  dev/console c 5 1
sudo mknod –m 660 dev/null c 1 3 

2)創建其它目錄

mkdir mnt tmp root

 

6.製作jffs2映像文件

由於mkfs.jffs2工具之前已經安裝好了,所以直接使用mkfs.jffs2命令:

cd /work/nfs_root/                 //返回到上個目錄
mkfs.jffs2 -n  -s 2048  -e 128KiB  -d fs_mini_mdev_new  -o fs_mini_mdev_new.jffs2
//-n:表示每塊不添加清除標記,-s:NAND的每頁為2k,-e: NAND的每塊為128kb
//-d fs_mini_mdev_new:表示要製作的根文件系統文件
//-o fs_mini_mdev_new.jffs2:表示生成的映像文件

7.燒寫jffs2,啟動內核

nfs 30000000 192.168.2.106:/work/nfs_root/fs_mini_mdev_new.jffs2
nand erase.part rootfs
nand write.jffs2 30000000 260000  $filesize
set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2
nfs 32000000 192.168.2.106:/work/nfs_root/uImage_new
bootm 32000000

7.1啟動內核

列印如下圖所示:

 

進入si,搜索exitcode,找到0x00000004對應的巨集定義是SIGILL,表示非法指令

是因為arm-linux-gcc-4.3.2是使用的EABI介面,內核由於未配置,所以出現非法

7.2 配置內核支持EABI
輸入make menuconfig,搜索EABI,找到位於:

kernel feature->

[*] Use the ARM EABI to compile the kernel         
make uImage

重新編譯燒寫內核就沒問題了

 

 

未完待續,下章學習如何使內核支持yaffs系統

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 需要添加引用 Microsoft.Office.Interop.Excel 有興趣的可以看看 https://www.cnblogs.com/junshijie/p/5292087.html 這篇文章,裡面有更詳細如何操作EXCEL。 ...
  • 最近在自覺python,看到了知乎上一篇文章(https://www.zhihu.com/question/20799742),在福利網上爬視頻。。。 由是我就開始跟著做了,但答主給的例子是基於python2.x的,而我開始學的是3.x,把print用法改了以後還是有很多模塊導入不了,新手又不知道該 ...
  • 第一步: 在命令行中輸入 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE 第二步: 根據用戶查找該用戶下workspace(工作空間): 輸入: tf workspaces /owner:[Account] /serve ...
  • #region 測試EPPlus插入圖片 public static void Createsheel2() { WebClient client = new WebClient(); var downloadUrl = FileOperater.GetDownloadUrl("f736cf1950 ...
  • EF 更新部分欄位寫法 1、EF預設是查詢出來,修改後保存; 2、設置不修改欄位的IsModified為false,此方法不需要先從資料庫查詢出實體來(最優方法): 3、使用 EntityFramework.Extended 擴展,缺點是EF的上下文日誌不能捕獲執行的sql,此方法也比較麻煩需要逐一 ...
  • 之前有跟第三方通訊合作,應為CRC表碼問題導致校驗出結果不一致,糾結了很久,最後直接採用CRC計算方式校驗才解決。 兩種方式貼,自行對比。 CRC校驗計算方法 查表方法 ...
  • 電腦中文件有很多種,我們知道實際存在電腦中的都是二進位。這裡我記錄了通過流對文件的讀取操作。 一、首先在這裡簡單涉及下位,位元組,字元的概念。 位(bit):可以表示0或1; 位元組(byte):由8位組成(bit),可以表示0-255,是256個不同的數據; 字元:字元根據編碼的不同有所區別; A ...
  • 一、Get 方式傳輸 二、POST 方式傳輸 其中PUT、DELETE方式跟上面基本相似。這裡就不再多說明 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...