Try   HackMD

Raspberry Pi Introduction (樹莓派介紹)

tags: Linux

目錄

開發板介紹

  • 樹莓派與GPIO腳位
    • 樹莓派是一塊入門款的嵌入式系統開發板

  • 週邊設備(下圖為PI4,僅供參考)
    • 配備有
      • USB - USB TypeA
      • Display - HDMI
      • Network - Ethernet RJ45 or WIFI
      • GPIO(General-purpose input/output) - 通用型輸入輸出
        • I2C
        • UART
        • SPI
        • Normal Input/Output

  • 使用示意圖
    • 你須要將放有系統的SD Card與要使用到的裝置插入(例如:鍵盤、滑鼠、網路線、螢幕線、電源線等)

  • 設定SD Card
    • 官方或自製的系統燒錄到SD Card中

編譯QEMU

由於使用 apt 工具安裝的 QEMU 並不支援 Pi 3,因此我們要自己手動編譯 2.12.0 版本的 QEMU

  • 安裝相關工具
    • apt-get install gcc build-essential automake gcc-arm-linux-gnueabihf vim git wget python pkg-config zlib1g-dev libglib2.0-dev libpixman-1-dev flex bison unzip libncurses5-dev
  • 下載 QEMU source code
    • $ wget https://download.qemu.org/qemu-2.12.0-rc3.tar.xz
  • 解壓縮
    • $ tar xvf qemu-2.12.0-rc3.tar.xz
  • 配置設定
    • $ cd qemu-2.12.0-rc3
    • $ ./configure --target-list=arm-softmmu,aarch64-softmmu
      • 相關設定
        • --target-list: 指令想要編譯的模擬器
        • --prefix: 指定程式要安裝的路徑
        • 其他設定與選項皆可從 ./configure --help 得知
  • 編譯及安裝
    • $ make -j$(nproc)
    • $ make install
  • 設定路徑
    • $ echo "export PATH=$(pwd)/aarch64-softmmu:\$PATH" >> ~/.bashrc
    • $ source ~/.bashrc

取得 Raspbian Image

  • 下載 Raspbian disk image 的 zip 檔
    • $ wget https://downloads.raspberrypi.org/raspbian/images/raspbian-2018-11-15/2018-11-13-raspbian-stretch.zip
  • 解壓縮
    • $ unzip 2018-11-13-raspbian-stretch.zip
    • 解壓縮後的檔名為 2018-11-13-raspbian-stretch.img

編譯 Raspberry Pi Kernel

由於 QEMU 和真實的 Pi 還是有些不同的,因此無法使用 2018-11-13-raspbian-stretch.zip 裡面提供的 kernel 來開機,需要自行編出 QEMU 用的 kernel

  • 下載 kernel source code
    • $ git clone git://github.com/raspberrypi/linux.git --branch raspberrypi-kernel_1.20180619-1 --single-branch --depth 1
  • 修正編譯上的錯誤
    • dhruvvyas90/qemu-rpi-kernel 上取得 linux-arm.patch
      • $ wget https://raw.githubusercontent.com/dhruvvyas90/qemu-rpi-kernel/master/tools/linux-arm.patch
    • 打上 patch
      • $ cd linux
      • $ patch -p1 < ./linux-arm.patch
  • 編譯 kernel
    • 編譯 kernel7.img
      • $ KERNEL=kernel7
    • 初始化 config
      • $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- versatile_defconfig
    • 設定 config
      • dhruvvyas90/qemu-rpi-kernel 上取得 config_fileconfig_ip_tables
        • $ wget https://raw.githubusercontent.com/dhruvvyas90/qemu-rpi-kernel/master/tools/config_file
        • $ wget https://raw.githubusercontent.com/dhruvvyas90/qemu-rpi-kernel/master/tools/config_ip_tables
      • 添加 config
        • ​​​​​​​​$ cat >> .config << EOF
          ​​​​​​​​CONFIG_CROSS_COMPILE="arm-linux-gnueabihf"
          ​​​​​​​​EOF
          
        • $ cat ./config_file >> .config
        • $ cat ./config_ip_tables >> .config
    • 開啟 menuconfig
      • $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
    • 編譯 kernel 和 dtb
      • $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bzImage dtbs
  • 設定路徑
    • $ cd ..
    • $ cp linux/arch/arm/boot/zImage ./kernel7.img
    • $ cp linux/arch/arm/boot/dts/versatile-pb.dtb ./versatile-pb.dtb
    • $ cp linux/vmlinux ./vmlinux

交叉編譯器(Cross Compiler)

有沒有發現上面編譯Raspberry Pi Kernel時有個參數CROSS_COMPILE=arm-linux-gnueabihf-呢?

  • 一般的編譯器

  • 那甚麼是交叉編譯器呢?
    你會將你在電腦上跑的程式直接放到手機上去跑嗎?不會吧

  • 為什麼我們要交叉編譯器呢? Code Ref

    • Hello World C Code
    ​​​​#include <stdio.h> ​​​​int main(void) { ​​​​ printf("Hello World\n"); ​​​​}
    • 我們一般的PC(x86_64)
    ​​​​.LC0: ​​​​ .string "Hello World" ​​​​main: ​​​​ push rbp ​​​​ mov rbp, rsp ​​​​ mov edi, OFFSET FLAT:.LC0 ​​​​ call puts ​​​​ mov eax, 0 ​​​​ pop rbp ​​​​ ret
    • 樹莓派(ARM)
    ​​​​.LC0: ​​​​ .ascii "Hello World\000" ​​​​main: ​​​​ stmfd sp!, {fp, lr} ​​​​ add fp, sp, #4 ​​​​ ldr r0, .L3 ​​​​ bl puts ​​​​ mov r3, #0 ​​​​ mov r0, r3 ​​​​ sub sp, fp, #4 ​​​​ ldmfd sp!, {fp, lr} ​​​​ bx lr ​​​​.L3: ​​​​ .word .LC0
  • 額外補充

執行 QEMU with Raspberry Pi 3

  • 如果你在Docker中執行

    ​​$ sudo qemu-system-arm                    \
    ​​  -M versatilepb                          \
    ​​  -cpu arm1176                            \
    ​​  -m 256                                  \
    ​​  -hda 2018-11-13-raspbian-stretch.img    \
    ​​  -kernel kernel7.img                     \
    ​​  -dtb versatile-pb.dtb                   \
    ​​  -net nic                                \
    ​​  -net user,hostfwd=tcp::5022-:22         \
    ​​  --append "root=/dev/sda2 panic=1"       \
    ​​  -vnc :0                                 \
    ​​  -no-reboot
    
    • 到外面Host執行
      $ vncviewer 0:5900
    • 如果沒裝的話
      $ sudo apt-get install vncviewer
  • 如果是在有圖型桌面環境下的話

    ​​$ sudo qemu-system-arm                    \
    ​​  -M versatilepb                          \
    ​​  -cpu arm1176                            \
    ​​  -m 256                                  \
    ​​  -hda 2018-11-13-raspbian-stretch.img    \
    ​​  -kernel kernel7.img                     \
    ​​  -dtb versatile-pb.dtb                   \
    ​​  -net nic                                \
    ​​  -net user,hostfwd=tcp::5022-:22         \
    ​​  --append "root=/dev/sda2 panic=1"       \
    ​​  -no-reboot
    
  • 如果是想要輸出到終端機的話

  $ sudo qemu-system-arm                                   \
    -M versatilepb                                         \
    -cpu arm1176                                           \
    -m 256                                                 \
    -hda 2018-11-13-raspbian-stretch.img                   \
    -kernel kernel7.img                                    \
    -dtb versatile-pb.dtb                                  \
    -net nic                                               \
    -net user,hostfwd=tcp::5022-:22                        \
    --append "rw console=ttyAMA root=/dev/sda2 panic=1"    \
    -nographic                                             \
    -no-reboot
  • Raspberry Pi 預設帳號密碼

    • 帳號︰pi
    • 密碼︰raspberry
  • 離開 QEMU

    • Ctrl-A + X

編譯GDB

由於使用 apt 工具安裝的 GDB 並不支援 ARM,因此我們要自己手動編譯 8.1 版本的 GDB

  • 下載 GDB soure code
  • 解壓縮
    • $ tar zxvf gdb-8.1.tar.gz
  • 配置設定
    • $ cd gdb-8.1
    • $ ./configure --target=arm-linux-gnueabihf
      • 相關設定
        • --target-list: 目標架構
        • --prefix: 指定程式要安裝的路徑
        • 其他設定與選項皆可從 ./configure --help 得知
  • 編譯及安裝
    • $ make -j$(nproc)
    • $ sudo make install
  • 設定路徑
    • $ echo "export PATH=$(pwd)/bin:\$PATH" >> ~/.bashrc
    • $ source ~/.bashrc

執行 QEMU with GDB

  • 啟動QEMU Debug Mode
    ​​$ sudo qemu-system-arm                    \ 
    ​​  -M versatilepb                          \
    ​​  -cpu arm1176                            \
    ​​  -m 256                                  \
    ​​  -hda 2018-11-13-raspbian-stretch.img    \
    ​​  -kernel kernel7.img                     \
    ​​  -dtb versatile-pb.dtb                   \
    ​​  -net nic                                \
    ​​  -net user,hostfwd=tcp::5022-:22         \
    ​​  --append "root=/dev/sda2 panic=1"       \
    ​​  -no-reboot                              \
    ​​  
    ​​  -serial stdio -S -s
    
  • 進行Debug
    • 啟動GDB
      $ arm-linux-gnueabihf-gdb vmlinux
    • 連線QEMU
      (gdb) target remote localhost:1234

Debug Tool - GDB 簡易教學

  • QEMU端參數
    qemu {OTHER_OPTION} -S -s(or -gdb tcp::1234)
    • -s(小寫 s):運行虛擬機時將 1234 端口開啟成調試端口,供 gdb 遠端除錯時使用
    • -S(大寫 S):啟動虛擬機時要「凍住」虛擬機,等待除錯器發出繼續運行的命令
    • -gdb tcp::1234:與-s相同
  • GDB指令
    • 連接到遠端:(gdb) target remote localhost:1234
    • 下中斷點:(gdb) b
    • 列出目前中斷點:(gdb) info b or (gdb) i b
    • 繼續執行直到下一個中斷點或結束:(gdb) continue or (gdb) c
    • 列出目前上下文:(gdb) list or (gdb) l
    • 單步 (會進入 funciton):(gdb) step or (gdb) s
    • 組語單步 (會進入 funciton):(gdb) si
    • 單步 (不會進入 funciton):(gdb) next or (gdb) n
    • 跳離一個 while for 迴圈:(gdb) until or (gdb) u
    • 顯示某變數:(gdb) print or (gdb) p,如 p str
    • 顯示 CPU 的 register:(gdb) info register or (gdb) i r
    • 顯示記憶體位置內容
      • 格式為x/nyz
      • 參數說明
      ​​​​​​​​n: 要印出的數量
      ​​​​​​​​y: 顯示的格式,可為C(char), d(整數), x(hex)
      ​​​​​​​​z: 單位,可為 b(byte), h(16bit), w(32bit)
      

本章節練習與反思

參考資料