2015年12月31日 星期四

製作 raspberry pi 2 linux 的檔案系統

如果要自己製作 linux 的檔案系統, 通常是一個小系統, 要不然官方提供的版本已經很好用了, 何必要自己做一個, 既然要小, 就會以 busybox 為主, 通常我都參考 build busybox and glic to root file system 來製作檔案系統。

busybox
wget http://busybox.net/downloads/busybox-1.23.2.tar.bz2
tar -xjf busybox-1.23.2.tar.bz2
cd busybox-1.23.2/
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install CONFIG_PREFIX=/home/export/rootfs

glibc

wget http://ftp.gnu.org/gnu/libc/glibc-2.21.tar.xz
tar -xJf glibc-2.21.tar.xz
mkdir glibc-build
cd glibc-build/
../glibc-2.21/configure arm-linux-gnueabi --target=arm-linux-gnueabi --build=i686-pc-linux-gnu --prefix= --enable-add-ons

在我使用的 toolchain 中, 要用以下的指令
../glibc-2.21/configure arm-linux-gnueabihf --target=arm-linux-gnueabihf --build=i686-pc-linux-gnu --prefix= --enable-add-ons
make
make install install_root=/home/export/rootfs

再安裝 glibc 後就會有一些工具可用, ldconfig, ldd 之類的, 對於查詢執行檔能不能正常運作很有用, 通常我會再安裝 bash。bash 的 cross compile 比較麻煩, 我用 native compile 搞定一個 static 版本。

然後再從 toolchain 複製 c++ library (libstdc++*) 到 root filesystem usr/lib, 其實這不是好方法, 比較正確的方式是從 source code 建立 toolchain, 再複製 toolchain 所需的 library 到 root filesystem, 可是因為太麻煩, 也不容易, 改用這樣的方式比較簡單。

刪除一些 glibc 多語系和 .a 的檔案。產生 /dev files (可能需要 root 權限), 從使用的 linux 複製過來即可。

wget https://matt.ucc.asn.au/dropbear/releases/dropbear-2015.68.tar.bz2
tar -xjf dropbear-2015.68.tar.bz2
cd dropbear-2015.68
./configure --host=arm-linux-gnueabi --prefix=/ --disable-zlib CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-ld
make
make install DESTDIR=/home/export/rootfs

我用 chroot 來做以下的 dropbear 相關設定 (x86), rfs 是 root file system 目錄:
chroot  rfs/ /bin/sh

產生 ssh key, 需要 /dev/urandom
mkdir /etc/dropbear
dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key  
dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key 

Unless otherwise specified, root will be given a default home directory of /home/root. However as this doesn't exist, DropBear will close your connection immediately after successfully logging in. To address this, simply create a home directory for root:
mkdir /home /home/root

產生密碼檔
touch /etc/passwd # /bin/bash 改成 /bin/sh
touch /etc/group
adduser root -u 0

建立 pty device, ref: http://www.denx.de/wiki/view/DULG/TelnetServerNotWorking
# mkdir /dev/pts
# mknod c 5 2 /dev/ptmx
# mount -t devpts devpts /dev/pts

DropBear can now be started by running:

dropbear -E 

同常都是用這個設定檔, 取消 busybox "Please press Enter to activate this console" 不需要按下 enter 即可進入 console

ref: linux开机自启动,去掉“Please press Enter to activate this console“


busyboxy /etc/inittab
 1 ::sysinit:/etc/init.d/rcS
 2
 3 # /bin/ash
 4 #
 5 # Start an "askfirst" shell on the serial port
 6 console::respawn:-/bin/ash
 7 #console::askfirst:-/bin/ash # Please press Enter to activate this console
 8 #console::askfirst:-/bin/login
 9
10 # Stuff to do when restarting the init process
11 ::restart:/sbin/init
12
13 # Stuff to do before rebooting
14 ::ctrlaltdel:/sbin/reboot
15 ::shutdown:/bin/umount -a -r
16 ::shutdown:/sbin/swapoff -a

我在幾個 arm cortex A9 平台上都很順利, 惟獨在 rpi2 上, 製作的檔案系統無法正常開到 login/shell 畫面。製作一個 linux 檔案系統的學問很大, 通常做完開不了機是很正常的, 至少有 10 種以上的原因會造成這個結果, 這次遇到一個我不知道的原因, 所以還要再加上一個。

錯誤訊息是這樣:

[ 3.052569] smsc95xx v1.0.4
[ 3.116771] smsc95xx 1-1.1:1.0: eth0: register 'smsc95xx' at usb-bcm2708_usb-1.1, smsc95xx USB 2.0 Ethernet, b8:27:eb:f7:69:6d

在開到網路這部份就 hang 住了, 把訊息拿去 google, 有一堆, 有的說要改 config.txt, 有的要改 cmdline.txt, 當然都無法解決我的問題, 我猜測了幾個原因:

  1. init 是不是又放錯位置了 (/sbin/init 才對)
  2. init 沒有編好, 無法正常執行
  3. init 需要的 library 沒有安置好
  4. /dev/ 沒有需要的 device file
  5. kernel 開機參數是不是有問題
  6. root filesystem 沒有正確 mount
  7. root filesystem 製作有問題
  8. kernel 沒有正確編譯
  9. sd partition 是不是有問題
  10. sd 檔案系統是不是有問題
  11. /etc/ 的設定是不是有問題

真的有 10 種以上的可能, 沒一個中 ...

linux_src/init/main.c L966 ~ 969 告訴我們 linux 怎麼啟動 init。

linux_src/init/main.c
 931 static int __ref kernel_init(void *unused)
 932 {
 933  int ret;
 934 
 935  kernel_init_freeable();
 936  /* need to finish all async __init code before freeing the memory */
 937  async_synchronize_full();
 938  free_initmem();
 939  mark_rodata_ro();
 940  system_state = SYSTEM_RUNNING;
 941  numa_default_policy();
 942 
 943  flush_delayed_fput();
 944 
 945  if (ramdisk_execute_command) {
 946   ret = run_init_process(ramdisk_execute_command);
 947   if (!ret)
 948    return 0;
 949   pr_err("Failed to execute %s (error %d)\n",
 950          ramdisk_execute_command, ret);
 951  }
 952 
 953  /*
 954   * We try each of these until one succeeds.
 955   *
 956   * The Bourne shell can be used instead of init if we are
 957   * trying to recover a really broken machine.
 958   */
 959  if (execute_command) {
 960   ret = run_init_process(execute_command);
 961   if (!ret)
 962    return 0;
 963   pr_err("Failed to execute %s (error %d).  Attempting defaults...\n",
 964    execute_command, ret);
 965  }
 966  if (!try_to_run_init_process("/sbin/init") ||
 967      !try_to_run_init_process("/etc/init") ||
 968      !try_to_run_init_process("/bin/init") ||
 969      !try_to_run_init_process("/bin/sh"))
 970   return 0;
 971 
 972  panic("No working init found.  Try passing init= option to kernel. "
 973        "See Linux Documentation/init.txt for guidance.");
 974 }

官方的檔案系統沒問題, 不過是 systemd 的設定, 不熟, 很難比對。

/etc/inittab L1 ~ L15 是原本的設定, 其結果是在 kernel 開機後就會直接進入 console, 不需要帳號/密碼。

/etc/inittab
 1 ::sysinit:/etc/init.d/rcS 
 2 
 3 # /bin/ash
 4 #
 5 # Start an "askfirst" shell on the serial port
 6 #console::askfirst:-/bin/ash
 7 #console::askfirst:-/bin/login
 8 
 9 # Stuff to do when restarting the init process
10 ::restart:/sbin/init
11 
12 # Stuff to do before rebooting
13 #::ctrlaltdel:/sbin/reboot
14 #::shutdown:/bin/umount -a -r
15 #::shutdown:/sbin/swapoff -a
16 
17 # /etc/inittab
18 #
19 # Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
20 #
21 # Note: BusyBox init doesn't support runlevels.  The runlevels field is
22 # completely ignored by BusyBox init. If you want runlevels, use
23 # sysvinit.
24 #
25 # Format for each entry: <id>:<runlevels>:<action>:<process>
26 #
27 # id        == tty to run on, or empty for /dev/console
28 # runlevels == ignored
29 # action    == one of sysinit, respawn, askfirst, wait, and once
30 # process   == program to run
31 
32 # Startup the system
33 null::sysinit:/bin/mount -t proc proc /proc
34 null::sysinit:/bin/mount -o remount,rw / # REMOUNT_ROOTFS_RW
35 null::sysinit:/bin/mkdir -p /dev/pts
36 null::sysinit:/bin/mkdir -p /dev/shm
37 null::sysinit:/bin/mount -a
38 null::sysinit:/bin/hostname -F /etc/hostname
39 # now run any rc scripts
40 ::sysinit:/etc/init.d/rcS
41 
42 # Put a getty on the serial port
43 tty1::respawn:/sbin/getty -L ttyAMA0 115200  # GENERIC_SERIAL
44 
45 # Stuff to do for the 3-finger salute
46 ::ctrlaltdel:/sbin/reboot
47 
48 # Stuff to do before rebooting
49 null::shutdown:/etc/init.d/rcK
50 null::shutdown:/bin/umount -a -r
51 null::shutdown:/sbin/swapoff -a

/etc/inir.d/rcS 可以加上:
mount -t proc none /proc
mount -t sysfs none /sys

不過最後卻需要/etc/inittab L43 的 ttyAMA0 這個設定才能正常以帳號/密碼的方式進入 console。所以還需要 /etc/passwd, /etc/group (從已經存在的 linux 系統複製即可) 記得把 root 密碼清除, 免得無法登入。

這花了我兩天才找到問題。

通常還可以複製 /dev/ 的裝置檔, 不用自己 mknod。

要製作 initfs 可以用以下指令:
cd rfs
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。