About me

Sunday, 16 April 2017

建立自己的作業系統 - step by step

之前研究過使用buildroot來建立一個客製化的Linux作業系統-『buildRoot study - 建立自己的作業系統』,那時候就想說想要把buildroot給抽掉,結果經過一年多,終於有空可以繼續之前的這個研究,這次完全的將buildroot給抽掉,『Bootloader-uboot』, 『Kernel』, 『Rootfs-busybox』,『glibc』完全都自己建立,藉由此了解一個小型OS的構成。




大綱

1.Preparation Ubuntu 數莓派2 TTL Cable Enviroment 2.目標 Firmware U-boot Kernel Root FileSystem 3.Partition SD Card 4.raspberry pi 官方軔體 5.Root File System 6.Glibc 7.Kernel 8.u-boot 9.Boot-up 10. Ref 1. Preparation
Ubuntu Host主機的部份,我是採用Ubuntu-16.04(x86_64),如果是其他Distributions,有些參數的話,可能就要自己注意一下去調整。 數莓派2 TTL Cable 這個天瓏書局有在賣,線怎麼接可以參考-『https://www.raspberrypi.com.tw/tag/usb-to-ttl/』。 Enviroment 1. 安裝基本的建制工具 $ sudo apt-get update $ sudo apt-get install g++ make gawk -y $ sudo apt-get install git-core libncurses5-dev vim -y $ sudo apt-get install wget python unzip bc -y $ sudo apt-get install device-tree-compiler -y 2 . 安裝arm cross compiler(建制『u-boot』和『busy-box』會用到) $ sudo apt-get install gcc-arm-linux-gnueabihf 3. 下載相關firmware會用到subversion,所以先安裝: $ sudo apt-get install subversion 4. 安裝kernel建制會用到的tool-chain 從git remote下載tool-chain $ git clone https://github.com/raspberrypi/tools 可將資料夾複製到比較方便的位置,像是官方的路徑是: /tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian 然後加入以下路徑到.bashrc底下最後一行: export PATH=:/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH 如果是64位元就用: export PATH=:/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH 2. 目標
上圖是之前文章『buildRoot study - 建立自己的作業系統』裡面的開機流程圖,之前幾乎所有的文件都是由buildroot產出的,而就像剛剛所說的,這一次則是要把所有的元件都自己建制出來,元件的分類如下: Firmware 就是上圖的『bootcode.bin』,『Start.elf』和『Fixup.dat』,雖然我們自己有建立u-boot來掌管開機,但是不幸的在pi2這邊還是無法第一站就直接交給我們的u-boot,還是必須要交給『bootcode.bin』。 U-boot 主要就是『u-boot.bin』這個檔案。 Kernel 主要就是『zImage』,還有『bcm2709-rpi-2-b.dtb』這個裝置樹檔案。 Root FileSystem 上圖的右半邊這次我們都要自己建置,主要有兩個部份: 1. buybox 2. glibc 這整個專案小弟我寫了個簡單的makefile,如果有需要的話可以參考一下: https://github.com/hugh712/my_pi2/tree/master/my_mini_pi 但是並沒有辦法寫的很完整,沒時間在很多平台上去測試,也沒辦法reuse,只能確保跟我同一個平台上的朋友第一次建制時,應該會成功而已,所以如果有什麼使用上的問題的話,請在跟我聯絡摟。 3. Partition SD Card
首先我們必須有一塊Micro SD,我們要將它分成兩個partition,其中第一個為boot section,另一個partition是放我們的rootfs的地方。 1. 假設路徑是/dev/sdd,先進入fdisk系統 $ sudo fdisk /dev/sdd 2. 將預設單位改成cylinders Command (m for help): u 3. 建立一個新的DOS格式的partition table Command (m for help): o 4. 建立放boot loader的partition (以下兩步的重點是規劃200MB的boot partition,其他的都規檔案系統用) Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): p (The new partition is a primary partition.) Partition number (1-4, default 1): (Press Enter for default.) Using default value 1 First cylinder (2-15193, default 2): 2 Last cylinder, +cylinders or +size{K,M,G} (2-15193, default 15193): +200M 5. 建立放檔案系統的partition Command (m for help): n Partition type: p primary (1 primary, 0 extended, 3 free) e extended Select (default p): p (The new partition is a primary partition.) Partition number (2-4, default 2): (Press Enter for default.) Using default value 2 First cylinder (2048-15193, default 2): 2048 Last cylinder, +cylinders or +size{K,M,G} (2-15193, default 15193): 15193 6. 將第一個磁區標注為bootable Command (m for help): a Partition number (1-4): 1 (Select Partition 1 to be active.) 7. 將第一個磁區改為FAT32 Command (m for help): t Selected partition 1 Hex code (type L to list codes): c Changed system type of partition 1 to c (W95 FAT32 (LBA)) 8.寫入新的partition table Command (m for help): w 9. 格式化新的boot load磁區為DOS FAT32 檔案系統 $ sudo mkfs.vfat -F32 /dev/sdd1 mkfs.vfat 3.0.13 (30 Jun 2012) 10.格式化第二個磁區為ext4檔案系統 $ sudo mkfs.ext4 /dev/sdd2 11. 如果可以的話,就順便掛載一下吧: $ sudo mkdir -p /mnt/boot $sudo mkdir -p /mnt/rtfs $sudo mount /dev/sdd1 /mnt/boot $sudo mount /dev/sdd2 /mnt/rtfs 這時就要用一下我上次的表示法,一樣這時是空的兩個partitons如下: 4. raspberry pi 官方軔體
有兩種方式取得: 1. https://github.com/raspberrypi/firmware/tree/master/boot 2. https://www.raspberrypi.org/downloads/raspbian/ (第二個為Ra pi 2的image file,如果想要把相對應的檔案取出來,請看之前文章『buildRoot study - 建立自己的作業系統』) 整個firmware下載下來大個恐怖,所以我只要抓我想要的部份就好,git的話一定要一次下載全部,還好發現可以利用svn來下載git的sub folder如下: $ svn export https://github.com/raspberrypi/firmware/trunk/boot firmware 抓完後我的mini_pi路徑應該會像這樣: 我們需要的檔案只有: start*.elf (GPU frimware) bootcode.bin ( bootloaders) Fixup.dat (一些修正檔) 但是應為我很懶,所以就直接將裡面的資料都複製到『/mnt/boot』底下: $ sudo cp firmware/* /mnt/boot 5. Root File System
Root File System的想法還是跟buildroot一樣,是使用busybox 來處理大部分user space的需求,流程如下: 1. 取得source code,branch為『1_26_stable』 $ git clone git://git.busybox.net/busybox -b 1_26_stable 2. 設定default config $ cd busybox $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig 如果你還想要修改任何設定的話,請用menuocnfig: $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig 3. 建制專案並且安裝 $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install 上面那個target『install』會在busybox的根目錄下安裝所有的輸出,你會發現多了一個資料夾叫做『_install』,內容的話就跟底下差不多:
當然是如果你有想要安裝的路徑你也可以用『CONFIG_PREFIX』來安裝,如下: $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=/path/to/folder 4. 建立其他的資料夾: 這時切換到剛剛的輸出資料夾『_install』底下,如同剛剛看到的,他的內容並不完全,也無法當成一個小型的root file system,所以這時需要建立其他的檔案和資料夾: $ cd _install $ mkdir bin sbin lib etc dev sys proc tmp var opt mnt usr home root media 5. 並於『etc』下面建立其他的資料夾和空的設定檔: $ cd etc touch inittab touch fstab touch profile touch passwd touch group touch shadow touch resolv.conf touch mdev.conf touch inetd.conf mkdir rc.d mkdir init.d touch init.d/rcS chmod +x init.d/rcS mkdir sysconfig touch sysconfig/HOSTNAME 6. 於『etc/inittab』寫入底下資料: ::sysinit:/etc/init.d/rcS ::askfirst:-/bin/sh ::ctrlaltdel:-/sbin/reboot ::shutdown:/bin/umount -a -r ::restart:/sbin/init 底下這個是官方的example還有解釋,這邊就請自己看吧,沒多少內容,也很簡單: https://git.busybox.net/busybox/tree/examples/inittab 7. 修改『etc/init.d/rcS』: #!/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel mount -a mkdir /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s /bin/hostname -F /etc/sysconfig/HOSTNAME ifconfig eth0 192.168.1.78 8. 於『etc/fstab』裡面加入底下內容: proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /var tmpfs defaults 0 0 tmpfs /tmp tmpfs defaults 0 0 tmpfs /dev tmpfs defaults 0 0 9. 於『dev』底下建立基本的裝置: cd dev mknod console c 5 1 chmod 777 console mknod null c 1 3 chmod 777 null 10. 將所有資料複製到剛剛建立出來的file system partition裡面: $ sudo cp -rf _install/* /mnt/rtfs 這一步之後,已經有『firmware』和一個小型的『root file system』了,表示圖如下:
6. Glibc
剛剛建立的busybox如果沒有相對應的函式庫的話,使用上會有問題,所以下一步需要建立的是負起跟kernel溝通的函式庫,這邊採用的是『glibc』,建制流程如下: 1. 下載tarball,並解壓縮,版本是採用『2.25』: mkdir glibc wget http://ftp.gnu.org/gnu/libc/glibc-2.25.tar.xz -P glibc cd glibc tar -xJf glibc-2.25.tar.xz
2. 建立建制資料夾,並且建制default config export TARGET=arm-linux-gnueabihf mkdir glibc-build cd glibc-build/ ../glibc-2.25/configure $TARGET --target=$TARGET --build=$MACHTYPE --prefix= --enable-add-ons 3. 建制glibc make 4. 將glibc安裝到root file system的partition上: $ sudo make install install_root=/mnt/rootfs
7. Kernel
接下來這一步要件置的是『Kernel』,流程如下: 1. 取得source code,branch是『rpi-4.9.y』。 $ git clone --depth=1 https://github.com/raspberrypi/linux -b rpi-4.9.y
2. 設定default config $ cd linux $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig 3. 建制專案,包含『zImage』,所有的modules,和dtbs(裝置樹檔案) $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs 4. 安裝相關模組到root file system上 $ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/mnt/rtfs modules_install 5. 複製kernel和DTB(Device Tree blobs)到boot partition上 $ sudo cp arch/arm/boot/dts/*.dtb /mnt/boot/ $ sudo cp arch/arm/boot/dts/overlays/*.dtb* /mnt/boot/overlays/ $ sudo cp arch/arm/boot/dts/overlays/README /mnt/boot/overlays/
8. u-boot
其實實際上不需要這個u-boot,這個自製的OS也是可以運作,因為firmware裡面的『bootcode.bin』就算是一個可以用的bootloader了,但是既然要自製了,那當然就是連bootloader也自己來阿,比較有快感XD。 1. 下載 source code,並且checkout到tag版本『v2017.03-rc3』 $ git clone git://git.denx.de/u-boot.git $ cd u-boot $ git checkout tags/v2017.03-rc3 -b v2017.03-rc3
2. 設定default config $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- rpi_2_defconfig 3. 建制u-boot $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all 4. build完之後,會產生一個『u-boot.bin』,將這個『u-boot.bin』放到boot section裡: $ sudo cp u-boot.bin /mnt/boot/ 5. 修改『config.txt』和『cmdline.txt』 在boot section裡,加入兩個檔案,『config.txt』和『cmdline.txt』,在檔案config.txt最下面加入: kernel=u-boot.bin 這樣『bootcode.bin』才會將控制權交給我們建出來的『u-boot』,而不是交給預設的『kernel』。而『cmdline.txt』則是要傳給kernel的相關參數,這兩個檔案可以參考一下我的檔案: https://github.com/hugh712/my_pi2/blob/master/my_mini_pi/configs/cmdline.txt https://github.com/hugh712/my_pi2/blob/master/my_mini_pi/configs/config.txt 在這個步驟後,理論上我們的拼圖已經湊齊如下圖了。
9. Boot-up
1. 啟動終端機程式 先安裝終端機 $ sudo apt-get install screen 然後啟動終端機,serial port的介面因人而異,我這邊是/dev/ttyUSB0,BR是115200 $ sudo screen /dev/ttyUSB0 115200 插上電後,終端機就會開始出現log了,如下
2. 手動開機 第一次可以先手動開機,藉由一個指令一個指令去了解做了什麼事。 # Tell Linux that it is booting on a Raspberry Pi2 setenv machid 0x00000c42 # Set the kernel boot command line setenv bootargs "earlyprintk console=tty0 console=ttyAMA0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait noinitrd" # Save these changes to u-boot's environment saveenv # Load the existing Linux kernel into RAM fatload mmc 0:1 ${kernel_addr_r} zImage fatload mmc 0:1 ${fdt_addr_r} bcm2709-rpi-2-b.dtb # Boot the kernel we have just loaded bootz ${kernel_addr_r} - ${fdt_addr_r}
上個畫面最後一步『Enter』給它按下去後,理論上你就可以看到一堆log了。最後會停在下面這個畫面上:
最後這個u-boot的開機步驟其實跟上一篇文章的差不多,唯一的差異就是要特別指定『dtb』,不然會無法成功的交接給kernel,
開機流程的話,因為上一篇文章已經有講解過了,所以這邊比較著重於將每個元件拼起來,不著重於流程,有需要的可以參考我上一篇文章摟。


10. Ref

a. Booting a Raspberry Pi2, with u-boot and HYP enabled b. Embedded Linux 嵌入式系統開發實務 c. RPi Serial Connection d. RPI U-boot e. buildroot f. Preparing a bootable SD card g. elinux.org_RPi_Advanced_Setup h. how-the-raspberry-pi-boots-up i. Raspberry Pi Kernel Compilation j. Loading Images with U-Boot k. Raspberry_Pi_Kernel_Compilation l. Raspberry_Pi_Kernel_Compilation_2 j. Cross_Compiling_BusyBox_for_ARM k. http://elinux.org/RPi_U-Boot

4 comments:

  1. 請問你有試過在raspberry pi3 bare metal programming嘛?,已經將firmware 資料夾裡面的東西都放到我的SD卡了,不過不知道怎麼寫出一個可以讓LED亮起來的kernel.img,請問有什麼建議嘛?

    ReplyDelete
    Replies
    1. 如果是on board的led,可以參考一下這一篇
      https://www.raspberrypi.org/forums/viewtopic.php?f=31&t=12530

      Delete
  2. 請問你有試過在raspberry pi3 bare metal programming嘛?,已經將firmware 資料夾裡面的東西都放到我的SD卡了,不過不知道怎麼寫出一個可以讓LED亮起來的kernel.img,請問有什麼建議嘛?

    ReplyDelete