2017년 7월 6일 목요일

PINE A64 보드로 알아보는 64-bit ARM Linux

이번 posting에서는 PINE A64 보드(Allwinner ARM Cortex-A53 64bit)를 기반으로한 64-bit ARM Linux에 관하여 소개하는 시간을 갖고자 한다. 개인적으로는 64-bit 보드를 사용해 보는 것은 이번이 처음인데, 32-bit인 경우에 비해 어떤 차이가 있는지 중점적으로 살펴 보기로 하자.
  OS

Board
SoC

<목차>
1. 머릿말
2. PINE A64 보드 소개
  => Board 개요 & OS image 설치
  => u-boot, kernel build and booting image 생성 방법 소개
3. Armbian 프로젝트 소개
  => ARM Linux OS 프로젝트
4. Allwinner A64의 이해 1: boot sequence & FEL mode 소개
  => BROM에서 u-boot loading까지의 과정 및 복구 모드 분석
5. Allwinner A64의 이해 2: u-boot 부팅 흐름 분석
  => u-boot 부팅 flow 분석
6. Allwinner A64의 이해 3: kernel 초기화 코드 분석
  => cpu0 부팅 흐름 분석, cpu1/2/3 부팅 흐름 분석
7. Allwinner A64의 이해 4: device Tree 분석
  => pine64 device tree 주요 내용 소개 
8. 맺음말


1. 머릿말
요즘은 주변에서 값싼 SBC(Single Board Computer)를 아주 쉽게 구할 수 있는 세상이 되었다. 이는 누구나 마음만 먹으면 아주 쉽게 embedded system 개발 환경을 구축할 수 있는 준비가 되었다는 것을 의미한다. Board를 손에 넣은 개발자라면, 그 다음은 당연히 bootloader, kernel, rootfs 등을 build해 보고, 부팅 가능한 OS image를 만들어 이를 target board에 올려 보고 싶어 할 것이다.

이번 posting에서는 이렇게 다양하게 쏳아져 나오는 SBC 중에서 Allwinner의 A64(64bit quad core) SoC를 채용하여 개발된 PINE A64 보드에 관하여 이모 저모를 따져보고자 한다.

  <PINE A64 보드 관련 소개 내용>
   a) 64-bit cross-toolchain, u-boot, kernel build 방법 소개
   b) booting sequence & FEL mode
   c) u-boot booting flow 분석
   d) kernel booting flow 분석
   e) 주요 device tree(CPU, Clock & PLL, PMIC) 분석

또한 embedded linux board를 언급하면서, 그에 걸맞는 build system을 언급하지 않을 수 없는데, 이번에는 앞서 여러 차례에 걸쳐 소개했던 Yocto project나 Buildroot 대신, (1)아주 간편한 방법을 통해 Debian 계열(예: Ubuntu)의 linux image를 만들 수 있는 방법인 armbian 프로젝트(2)Android의 PC version으로 출시된 Remix OS에 관해서도 간략히 소개하는 시간을 갖도록 하겠다.

참고사항: Allwinner chip을 사용한 SBC는 그 종류가 매우 많다(예를 들어, cubieboard, bananapi, orangepi, nanopi, pine64 ...). 모르긴 몰라도 국내에서도 많은 업체에서 Allwinner chip을 사용해 제품 개발을 하고 있는 것으로 보인다.

마지막으로 이번 posting을 위해 사용한 Host PC는 Ubuntu 16.04(64-bit) 환경임을 밝힌다.


2. PINE A64 보드 소개
이번 절에서는 Pine Microsystems Inc.에서 Kickstarter 캠페인의 일환으로 개발하여 판매 중인 $15(~ $29) 짜리 PINE A64 board를 소개해 보고자 한다.

2.1 PINE A64(+) 보드 개요

그림 2.1 PINE A64(+) 보드 외관(출처: www.pine64.org)

참고 사항: PINE A64는 PINE A64 512MB($15), PINE A64+ 1GB($19), PINE A64+ 2GB($29)의 세 종류가 있다.

PINE A64(+) 보드는 Allwinner사의 A64 SoC(Quad-Core ARM Cortex-A53)를 사용하고 있는데, A64의 주요 특징을 열거해 보면 표 2.1과 같다.

그림 2.2 Allwinner A64 processor(출처: sunxi web site)

그림 2.3 Allwinner A64 processor block도(출처: 참고문헌 [6]에서 발췌)

표 2.1 - Allwinner A64 주요 특징(출처: A64 brief documentation)

참고로 A64는 Allwinner의 여러 SoC 중 H3과 매우 많이 닮아 있다. 즉, H3의 Cortex-A7 core를 Cortex-A53 core로 교체한 것이 A64라고 보면 된다. 물론 주변 장치 controller 중 일부(USB host controller, DRAM controller, UART, MMC controller, SRAM & BROM mapped address, pinmux configuration 등)는 차이가 있지만 말이다.

한편 Allwinner SoC의 경우는 Sunxi라는 open source S/W community가 활성화되어 있어, sunxi-linux, sunxi-uboot, sunxi-tools 등을 제공 받을 수 있다. 또한 mainline kernel(최신 4.x kernel)에서도 이미 Allwinner SoC을 지원하기 위한 움직임이 활발히 진행되고 있다. 따라서 이 두가지 source code를 참조하여 자신의 project에 맞게 활용하면 될 것으로 보인다.

그림 2.4 SUNXI community for Allwinner chipsets

참고 사항: sunxi kernel 3.4 version의 경우는 device tree 대신 FEX라는 방법(allwinner에서 device tree를 대신하여 사용하던 방법)을 사용하고 있다.

마지막으로 PINE A64(+) 보드는 아래 그림과 같이 Ubuntu, Android 등 현재 다양한 OS를 지원하고 있으므로, 이중 적당한 것을 선택하여 사용하면 될 것으로 보인다(실제로는 아래 내용 보다 더 많은 OS가 지원되고 있으나, 여기서는 중요한 부분만 표시해 보았음).

그림 2.5 PINE A64(+) board가 지원하는 OS(일부 내용)

여기서 잠깐 ! Allwinner A64 SoC을 이용한 64-bit SBC 보드 종류
------------------------------------------------------------------------------------------------------------------------------------------------------------
가격대 성능비가 뛰어난 Allwinner A64 SoC를 활용한 SBC는 다양한 종류가 있는데, 이를 열거해 보면 다음과 같다.

a) Azpen hybrx : Remix OS를 탑재한 notebook
e) Remix Mini : Remix OS를 탑재한 Mini PC
...

참고사항: Remix OS란 ?

Android를 mobile 기기가 아닌, PC 용 운영체제로 사용할 목적으로 Jide 사(Google 출신이 모여 설립한 회사)가 개발한 OS(아주 흥미로움^^)로 자세한 사항은 아래 site를 참조하기 바란다.

------------------------------------------------------------------------------------------------------------------------------------------------------------

2.2 PINE A64(+) 보드에 OS 설치하기
PINE A64(+) 보드는 NAND나  eMMC가 아닌 microSD로 부팅하는데, 보드 가격에 microSD는 포함되어 있지 않으므로 별도로 준비해야만 한다.

a) PINE64 Installer로 booting OS image 설치하기
먼저  아래 site에서 PINE64 installer program을 download 받도록 하자.

참고 사항: PINE64 Installer는 Etcher라는 tool을 자신들의 보드에 맞게 개선한 program이다.

$ unzip pine64-installer-2.0.0-beta.2-linux-x64.zip
$ cd pine64-installer-2.0.0-beta.2-linux-x64
$ ./pine64-installer-2.0.0-beta.2-linux-x64.AppImage
  => 앞서 그림 2.5에서 언급한 다양한 OS image를 network으로 부터 내려 받아 자동으로 microSD에 설치해 주는 아주 편리한 tool임.
  => 여러 OS 중, 가장 궁금한 Remix OS를 설치해 보기로 하자.

그림 2.6 PINE64 Installer 실행 모습 - Remix OS 선택/8GB microSD 카드를 PC에 연결/Flash 버튼 선택(file download/flashing)

참고 사항: 설치 시간이 생각보다 오래 걸릴 수 있으니, 주의하기 바란다.

b) Console 설정 및 부팅하기
microSD 카드에 OS 이미지를 설치하였다면, 이제부터는 console cable을 연결한 후, 부팅을 시도해 보도록 하자.


그림 2.7 PINE A64(+) board 콘솔 연결(참고문헌 [18]에서 발췌)

참고 사항: 콘솔을 연결하기 위해서는 두가지 방법이 있는데, 필자는 EXP port(그림 우측 포트)를 이용하여 연결하였다.


그림 2.8 PINE A64(+) 보드 연결 모습 - USB(power), serial console, HDMI 등 연결

참고 사항: Wi-Fi 802.11BGN/BT 4.0 module 연결 방법은 간단한 사항이라 별도로 정리하지 않았다.


  
그림 2.9 PINE A64(+) 보드 부팅 모습 - minicom 115200,8N1

HDMI를 통해 출력된 Remix OS 전체 화면은 다음과 같다.

그림 2.10 Remix OS 구동 모습 

지금까지 PINE A64(+) 보드에 Remix OS를 설치하여 정상 동작하는 것을 확인해 보았으니, 이제 부터는 booting image(단, Remix OS가 아님)를 직접  만드는 방법을 고민해 보기로 하자.

2.3 longsleep's Github을 이용한 부팅 image(Ubuntu base image) 생성 방법
  => pine64 board를 위한 linux 부팅 image를 생성하기 위해  pine64.org page에 언급되어 있는 longsleep(nickname)이 작업한 내용을 따라가 보기로 하자.
  => longsleep이 사용한 kernel version은 3.10.105이다.
  => 이 글을 쓰는 현재(2017.7월) 아직은 buildroot 최신 버젼에 pine a64가 반영되지 않은 듯 보인다.

a) prebuilt 이미지로 Linux 부팅 이미지 생성하기
$ git clone https://github.com/longsleep/build-pine64-image
$ cd build-pine64-image

다음으로 아래 site에서 아래 두개 파일을  download 하도록 한다.
https://www.stdin.xyz/downloads/people/longsleep/pine64-images/
  => simpleimage-pine64-latest.img.xz
  => linux-pine64-latest.tar.xz
주의: 이 파일을 사용한다는 미리 build해 둔 내용을 사용하여 booting image를 만들겠다는 뜻임.

$ sudo ./build-pine64-image.sh ./simpleimage-pine64-latest.img.xz ./linux-pine64-latest.tar.xz
  => 중간에 에러 발생하여 아래 package 설치함.

$ sudo apt-get install kpartx
$ sudo apt-get install bsdtar

$ sudo ./build-pine64-image.sh ./simpleimage-pine64-latest.img.xz ./linux-pine64-latest.tar.xz
...

chyi@earth:~/IoT/Pine64/build-pine64-image$ ls -l
합계 15067680
-rw-rw-r-- 1 chyi chyi       1103  6월 20 12:52 LICENSE.txt
-rw-rw-r-- 1 chyi chyi        213  6월 20 12:52 README.md
drwxrwxr-x 2 chyi chyi       4096  6월 20 12:52 blobs
drwxrwxr-x 3 chyi chyi       4096  6월 20 12:52 bootlogo
drwxr-xr-x 2 root root       4096  6월 20 14:18 build
-rwxrwxr-x 1 chyi chyi       2079  6월 20 12:52 build-pine64-image.sh
-rwxrwxr-x 1 chyi chyi       1174  6월 20 12:52 convert-pine64-image.sh
drwxrwxr-x 2 chyi chyi       4096  6월 20 12:52 kernel
-rw-rw-r-- 1 chyi chyi   57800288  6월 20 14:17 linux-pine64-latest.tar.xz
drwxrwxr-x 5 chyi chyi       4096  6월 20 12:52 simpleimage
-rw-rw-r-- 1 chyi chyi    5065592  6월 20 14:16 simpleimage-pine64-latest.img.xz
drwxrwxr-x 4 chyi chyi       4096  6월 20 12:52 snappy
drwxrwxr-x 2 chyi chyi       4096  6월 20 12:52 u-boot-postprocess
-rw-r--r-- 1 root root 3932160000  6월 20 14:18 xenial-pine64-bspkernel-20170620_141756_KST-1.img
-rw-r--r-- 1 root root 3932160000  6월 20 15:38 xenial-pine64-bspkernel-20170620_153737_KST-1.img
-rw-r--r-- 1 root root 3932160000  6월 20 15:43 xenial-pine64-bspkernel-20170620_154244_KST-1.img
-rw-r--r-- 1 root root 3932160000  6월 20 16:16 xenial-pine64-bspkernel-20170620_154432_KST-1.img
  => 생성된 image file로 부팅을 시도해 보자.

b) longsleep's image로 부팅해 보기
dd 명령을 사용하여 아래와 같이 microSD(주의: /dev/sdb는 실제 각자의 상황에 맞는 내용으로 교체해 주어야 함)에 img 파일을 write해 주자.

sudo dd if=./xenial-pine64-bspkernel-20170620_141756_KST-1.img of=/dev/sdb bs=1M
3750+0 레코드 들어옴
3750+0 레코드 나감
3932160000 bytes (3.9 GB, 3.7 GiB) copied, 634.211 s, 6.2 MB/s

microSD를 PINE A64 보드에 꽂은 후 부팅을 시도해 보니, 아래와 같이 정상적으로 부팅이 된다.

 
그림 2.11 longsleep image로 부팅하는 모습

c) 개별 코드를 build하여 booting 이미지 생성하기
사전에 longsleep이 build해 둔 binary 파일을 가지고 booting image를 만들어 보았으니, 이제 부터는 booting에 필요한  ATF, u-boot, device tree, kernel 등을 직접 build한 후, 이를 토대로  booting 이미지를 만들어 보도록 하자. 관련해서는 아래 site의 내용을 참조하기로 한다.

<u-boot build 및 ATF & FDT를 통합한 이미지 생성 절차>

<kernel build 및 initrd 생성 절차>

<pine64를 위한 simple image 생성 절차>

c-1) 64bit cross-toolchain 설치하기
64-bit ARM용 cross-toolchain 파일을 아래와 같이 linaro site에서 직접 download한다.

예) https://releases.linaro.org/components/toolchain/binaries/5.3-2016.05/aarch64-linux-gnu/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu.tar.xz

sudo mkdir -p /opt/arm64
sudo tar xvf gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu.tar.xz -C /opt/arm64
  => 64bit cross-toolchain을 /opt/arm64 디렉토리에 설치한다(푼다).

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export PATH=/opt/arm64/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/:$PATH
  => toolchain 환경 변수 및 path를 지정한다.

참고 사항: kernel과는 달리 u-boot은 여전히 32bit cross-compile을 해야한다.

c-2) u-boot만 build 하기
$ cd build-pine64-image
$ git clone --depth 1 --branch pine64-hacks --single-branch https://github.com/longsleep/u-boot-pine64.git u-boot-pine64
  => u-boot source code를 내려 받는다.
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun50iw1p1_config
  => u-boot config를 적용한다.
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  => u-boot.bin(32-bit) 파일이 결과로 생성되지만, 실제 사용을 위해서는 아래 c-5)의 과정을 거쳐야 한다.

c-3) ATF(ARM Trusted Firmware) build 하기
$ cd build-pine64-image
$ git clone --branch allwinner-a64-bsp --single-branch https://github.com/longsleep/arm-trusted-firmware.git arm-trusted-firmware-pine64
$ make clean
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- PLAT=sun50iw1p1 bl31
  => build/sun50iw1p1/release/bl31.bin

c-4) sunxi pack tools build 하기
cd build-pine64-image
$ git clone https://github.com/longsleep/sunxi-pack-tools.git sunxi-pack-tools
$ make -C sunxi-pack-tools
  => sunxi pack tool은 Allwinner BSP에 포함되어 있는 내용으로 cross-compile하지 않고, Host build를 한다.

c-5) u-boot과 ATF 및 FDT(device tree)를 통합하기 - boot0가 인식 가능하도록 하기 위한 절차
$ cd u-boot-postprocess
$ sudo ./u-boot-postprocess.sh
  => ../build/u-boot-with-dtb.bin 파일이 생성됨.

c-6) kernel만 build 하기
cd build-pine64-image
$ git clone --depth 1 --branch pine64-hacks-1.2 --single-branch https://github.com/longsleep/linux-pine64.git linux-pine64
  => kernel source download
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- sun50iw1p1smp_linux_defconfig
  => kernel config 적용
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
  => kernel config  조정

그림 2.12 kernel menuconfig

$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LOCALVERSION= clean
  => make clean
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4 LOCALVERSION= Image
  => arch/arm64/boot/Image 파일 생성(64-bit)
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4 LOCALVERSION= modules
  => module build

참고 사항: 일반적인 방식과는 달리, 이 단계에서 dtb 파일을 생성하지 않고, 아래 c-8) install_kernel.sh script 실행 시 blobs/pine64.dts 파일로 부터 dtb 파일을 생성하도록 한다.
참고 사항: graphics driver(mali.ko) 생성과 관련해서는 일단 배제하였다.

c-7) busybox로 initrd 만들기
cd build-pine64-image
git clone --depth 1 --branch 1_24_stable --single-branch git://git.busybox.net/busybox busybox
$ cd busybox
$ cp ../kernel/pine64_config_busybox .config
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4 oldconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4
$ cd ../kernel
./make_initrd.sh
  => initrd.gz 파일 생성됨.

c-8) kernel, dtb, initrd.img를 적당한 folder로 복사하기
$ sudo ./install_kernel.sh - ../linux-pine64
  => ../build 아래에 결과물 복사(pine64 디렉토리)

그림 2.13 longsleep build 결과물 확인(중간 결과)

c-9) pine64를 위한 최종 이미지 생성하기<TBD>
$ cd kernel
$ ./make_simpleimage.sh my-simpleimage.img
$ xz my-simpleimage.img

참고 사항: 편의상 linux-pine64-latest.tar.xz은 압축을 푼 상태에서, 위에서 build하여 얻은 내용 즉, uImage, dtb file 등으로 교체 후, 다시 묶어 재 생성한 것을 사용하기로 한다.

<최종 image 생성>
$ cd ..
$ sudo ./build-pine64-image.sh ./my-simpleimage.img.xz ./linux-pine64-latest.tar.xz
  => <TBD> 이렇게 해서 만들어진 이미지가 정상 동작하는지 확인 필요함.


3. armbian 프로젝트 소개
2~3년전 쯤에 Allwinner사의 A20(ARM Cortex-A7 dual core) chipset을 이용하여 옥외형 LTE 라우터를 개발한 적이 있다. 그 때만 해도 Armbian의 존재를 몰랐던 탓에 rootfs를 구성하기 위해 일일이 package 관련 source code를 download 받은 후, 개별적으로 build하고 테스트했던 기억이 난다(좀 다른 이유 -  sunxi lichee를 기반으로 작업 - 도 있었긴 했지만 말이다^^). 물론 yocto project나 buildroot 혹은 openwrt(gateway 장비 개발용)와 같은 훌륭한 build system이 이미 존재하는 상황이기는 하지만, 오늘 소개하는 Armbian도 아주 간단한 방법으로 ARM용 debian 배포판을 만들어 준다는 점에서 매우 주목할만한 가치가 있다고 하겠다(실제로 Allwinner SoC을 기반으로 하는 보드는 armbian을 모두 지원하고 있음).

Armbian이 지원되는 ARM board의 종류는 아래와 같이 아주 다양하다.

 ...
그림 3.1 armbian을 지원하는 보드 종류(출처: www.armbian.com)

  • Allwinner A10, A20, A31, H2+, H3, H5, A64
  • Amlogic S805 and S905 (Odroid boards), S802/S812, S805, S905, S905X and S912
  • Actionsemi S500
  • Freescale / NXP iMx6
  • Marvell Armada A380
  • Samsung Exynos 5422
  • ...
이번 절에서는 PINE A64 board를 target으로 armbian image를 생성하는 과정을 정리해 보고자 한다.

3.1 Armbian build 절차
Armbian을 build하기 위해서는 반드시(이 글을 쓰고 있는 현재 기준) Ubuntu Xenial 16.04(64bit) 환경이 준비되어 있어야 한다.

$ git clone https://github.com/igorpecovnik/lib --depth 1
  => armbian source code를 내려 받는다.
cp lib/compile.sh .
  => lib/compile.sh 파일을 현재 directory로 복사해 온다.
$ ./compile.sh
  => armbian build를 진행한다.
  => 제일 먼저 64-bit cross-toolchain을 받아서 설치한다.

[ warn ] This script requires root privileges
[sudo] password for chyi: 
[ o.k. ] This script will try to update
Already up-to-date.
이미 'master'에 있습니다
브랜치가 'origin/master'에 맞게 업데이트된 상태입니다.
[ o.k. ] Preparing [ host ]
[ o.k. ] Build host OS release [ xenial ]
[ .... ] Downloading [ gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux ]
######################################################################## 100.0%
######################################################################## 100.0%
[ .... ] Verifying 
gpg: keyring `/home/chyi/IoT/Pine64/armbian_test/output/.gpg/pubring.gpg' created
gpg: /home/chyi/IoT/Pine64/armbian_test/output/.gpg/trustdb.gpg: trustdb created
gpg: error reading key: public key not found
gpg: keyring `/home/chyi/IoT/Pine64/armbian_test/output/.gpg/secring.gpg' created
gpg: requesting key 8F427EAF from hkp server keyserver.ubuntu.com
gpg: key 8F427EAF: public key "Linaro Toolchain Builder <michael.hope+cbuild@linaro.org>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)
gpg: assuming signed data in `gcc-linaro-arm-linux-gnueabihf-4.8-2014.04_linux.tar.xz'
gpg: Signature made 2014년 04월 17일 (목)  using RSA key ID 8F427EAF
gpg: Good signature from "Linaro Toolchain Builder <michael.hope+cbuild@linaro.org>"
[ .... ] Extracting 
[ o.k. ] Download complete 
[ .... ] Downloading [ gcc-linaro-4.9.4-2017.01-x86_64_aarch64-linux-gnu ]
######################################################################## 100.0%
######################################################################## 100.0%
[ .... ] Verifying 
[ .... ] Extracting 
[ o.k. ] Download complete 
[ .... ] Downloading [ gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi ]
######################################################################## 100.0%
######################################################################## 100.0%
[ .... ] Verifying 
[ .... ] Extracting 
[ o.k. ] Download complete 
[ .... ] Downloading [ gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf ]
######################################################################## 100.0%
######################################################################## 100.0%
[ .... ] Verifying 
[ .... ] Extracting 
[ o.k. ] Download complete 
[ .... ] Downloading [ gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf ]
...

  => build 중 아래와 같은 menu 선택화면이 뜨게 된다.
  => 아래 그림 3.2 ~ 3.4의 그림은 첫번째 menu를 선택할 경우를 보여준다.

그림 3.2 kernel & u-boot 패키지(.deb) 생성 - 메뉴 선택


그림 3.3 보드 선택 화면 - pine64 보드 선택 예


그림 3.4 kernel branch 선택 화면 - default 선택 예

  => 이후 build 작업을 계속 진행한다.

[ o.k. ] Syncing clock [ host ]
[ o.k. ] Downloading sources 
[ o.k. ] Checking git sources [ u-boot-pine64 master ]
[ .... ] Creating local copy 
[ .... ] Fetching updates 
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 16 (delta 4), reused 12 (delta 4), pack-reused 0
오브젝트 묶음 푸는 중: 100% (16/16), 완료.
https://github.com/armbian/u-boot-pine64-legacy URL에서
 * branch            master     -> FETCH_HEAD
 * [새로운 브랜치]   master     -> origin/master
[ .... ] Checking out 
[ o.k. ] Updating submodules 
[ o.k. ] Checking git sources [ u-boot-pine64/master/u-boot-pine64 pine64-hacks ]
[ .... ] Creating local copy 
[ .... ] Fetching updates 
remote: Counting objects: 9807, done.
...

그림 3.5 1차 build 결과물

이후, 다시 compile.sh을 실행한 상태에서 두번째 메뉴를 선택하여 sd card용 image를 만들어 보도록 하자.
참고: 앞선 단계 즉, 그림 3.2 ~ 3.4를 생략하고 바로, 아래 단계를 바로 진행해도 된다.

./compile.sh

그림 3.6 sdcard에 설치할 image 파일 생성 메뉴 선택

그림 3.7 board 선택 - pine64 선택 예

그림 3.8 Target OS release 선택 - Jessie 선택 예


그림 3.9 image type 선택 - server 선택 예

  => 이후 한참 동안 build 작업을 진행하여, 최종 image 파일을 생성해 내게 된다.

Unpacking sunxi-tools (1.4.2-1~armbian5.25+1) ...
Setting up sunxi-tools (1.4.2-1~armbian5.25+1) ...
[ o.k. ] Calling image customization script [ customize-image.sh ]
[ o.k. ] Preparing image file for rootfs [ pine64 jessie ]
[ o.k. ] Current rootfs size [ 884 MiB ]
[ o.k. ] Creating blank image for rootfs [ 1368 MiB ]
1.34GiB [ 223MiB/s] [=====================================================================>] 100%
[ o.k. ] Creating partitions [ root: ext4 ]
[ .... ] Creating rootfs [ ext4 ]
[ o.k. ] Copying files to image [ sdcard-default-pine64-jessie-no.raw ]
        799.23M  99%   71.26MB/s    0:00:10 (xfr#37072, to-chk=0/47063)   

sent 802.04M bytes  received 747.42K bytes  69.81M bytes/sec

total size is 803.81M  speedup is 1.00
[ o.k. ] Copying files to /boot partition [ sdcard-default-pine64-jessie-no.raw ]
         38.55M  99%  262.39MB/s    0:00:00 (xfr#21, to-chk=0/28) 

sent 38.56M bytes  received 445 bytes  77.13M bytes/sec

total size is 38.55M  speedup is 1.00
[ o.k. ] Free space: [ SD card ]
tmpfs           1.5G  884M  617M  59% /home/chyi/IoT/Pine64/armbian_test/output/cache/sdcard-default-pine64-jessie-no
/dev/loop0p1    1.3G  909M  361M  72% /home/chyi/IoT/Pine64/armbian_test/output/cache/mount-default-pine64-jessie-no
[ o.k. ] Writing U-boot bootloader [ /dev/loop0 ]
[ o.k. ] Done building [ /home/chyi/IoT/Pine64/armbian_test/output/images/Armbian_5.30_Pine64_Debian_jessie_default_3.10.105.img ]
[ o.k. ] Runtime [ 32 min ]

그림 3.10 최종 build 결과물 - armbian image 파일

3.2 armbian build process 요약
compile.sh을 실행할 때 자동으로 이루어지는 일을 요약해 보면 다음과 같다.
   step-1) Ubuntu 16.04 LTS 상에 개발을 위한 환경 설정을 한다.
   step-2) armbian source를 dowload한 후, patch를 적용하고 검증된 configuration을 적용한다.
   step-3) u-boot, kernel, driver 및 기타 tool 들을 cross-compile한다.
   step-4) kernel, u-boot, dtb 및 rootfs를 debs 파일 형태로 묶는다.
   step-5) debootstrap 명령을 사용하여 Debian Wheezy, Jessie 혹은 Ubuntu Trusty image(SD card용 image)를 생성해 낸다.
   step-6) 추가 패키지를 설치하고, image size를 줄인다.

 아래 내용은 compile.sh의 대략적인 코드 흐름(u-boot, kernel build, rootfs 생성 및 전체 img 파일 생성 등) 정리한 것이다.

----------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------
코드 3.1 armbian build process 요약

3.3 compile.sh 추가 option 정리
compile.sh은 아래와 같이 다양한 option을 주어 실행할 수도 있다. 이 절에서는 compile.sh에 줄 수 있는 주요 option과 그 의미를 파악해 보도록 하자.

$ ./compile.sh BRANCH=default BOARD=pine64 KERNEL_ONLY=yes PROGRESS_DISPLAY=plain RELEASE=jessie

-----------------------------------------------------------------------------------------------------------------------------------------------------------
<compile.sh 주요 option 정리>
KERNEL_ONY=yes|no
  - 이 option을 사용하지 않을 경우,  메뉴 선택 창이 표시될 것임.
  - yes로 설정할 경우, kernel, u-boot 및 armbian system을 설치하기 위해 필요한 패키지만을 compile하게 됨.
  - no로 설정할 경우, SD card에 들어가는 전체 OS image를 build하게 됨.

KERNEL_CONFIGURE=yes|no
  - yes로 설정할 경우, build하는 동안에 kernel configuration menu가 뜨게 될 것임.
  - no로 지정할 경우는 kernel configuration을 변경하는 과정 없이 kernel을 compile하게 됨.

CLEAN_LEVEL=comma-separated list
  => 무엇을 clean할지를 정한다.
  - make : kernel, u-boot source에 대해 make clean하는 효과를 준다.
  - images : output/images 디렉토리를 지운다.
  - debs : 현재 branch 및 device family에 대해 output/debs 아래의 패키지를 지운다.
  - alldebs : output/debs 아래의 모든 패키지를 지운다.
  - cache : output/cache를 지운다.
  - sources : 다운로드한 모든 source를 지운다.
  - extras : output/debs/extra/ 아래에 있는 additional 패키지를 지운다.

BUILD_DESKTOP=yes|no
  - yes로 설정할 경우, desktop용 image를 build한다.
  - no로 지정할 경우, console interface를 갖는 image를 만들어 낸다.

BOARD=string
  - board 명을 지정하는 메뉴 창을 띄우지 않고 board 명을 바로 지정한다.

BRANCH=default|next|dev
  - kernel과 u-boot의 branch를 곧 바로 지정한다(역시 메뉴 창을 띄우지 않게됨).

RELEASE=wheezy|jessie|trusty|xenial
  - OS release 명령 직접 설정한다(역시 메뉴 창을 띄우지 않게됨).
-----------------------------------------------------------------------------------------------------------------------------------------------------------

기타 자세한 option과 관련해서는 아래 web page의 내용을 확인해 보기 바란다.


여기서 잠깐 ! Robert Nelson's wiki site 소개
Armbian과 유사한 프로젝트로, Digi-Key에 근무하는 Robert C. Nelson은 다양한 ARM board(예: BBB, SAMA5D3, Wanboard, Zynq 7000 등)에 맞는 Linux image를 자동으로 생성해 주는 project(꽤 괜찮은 site임)를 운용하고 있다. Armbian과 비교해 어떤 차이가 있는지 각자 살펴보기 바란다.
-----------------------------------------------------------------------------------------------------------------------------------------------------------

3.4 PINE A64(+) 보드에서 armbian image 올려 보기
그림 3.10의 최종 결과물을 dd 명령 혹은 PINE64 Installer program으로 microSD에 설치한 후, 부팅을 시도해 보기로 하자.

그림 3.11 armbian 디렉토리 구조 

<dd 사용 예>
$ sudo dd if=./Armbian_5.30_Pine64_Debian_jessie_default_3.10.105.img of=/dev/sdb bs=1M
1368+0 레코드 들어옴
1368+0 레코드 나감
1434451968 bytes (1.4 GB, 1.3 GiB) copied, 70.7294 s, 20.3 MB/s

Target board를 부팅한 상태에서 cpu info, interrupts 정보 등을 확인해 보면 다음과 같다.

그림 3.12 PINE A64 cpu info


아래 그림은 Armbian_5.30_Pine64_Debian_jessie_default_3.10.105.img가 아니라, PINE64 Installer로 Armbian Ubuntu desktop version을 설치한 후 booting한 모습에 해당한다(Armbian desktop의 모양을 보여주기 위해 화면 capture를 해 보았음).


그림 3.13 armbian OS 부팅 모습(PINE64 Installer로 설치 - Ubuntu desktop version)

3.5 armbian image에 사용자가 원하는 내용 추가하기
특정 build 시스템을 사용하다 보면, 언제나 아래와 같은 상황에 맞닥드리게 된다.

   내가 만는 package를 어떻게 추가할수 있을까 ?
   u-boot을 수정했는데 어떻게 적용하면 될까 ?
   kernel configuration을 수정했는데 어떻게 적용할까 ?
   ...

이러한 문제를 해결하기 위해 armbian은 userpatches/ 디렉토리를 제공하고 있다. 가령, 사용자가 원하는 package를 추가하고자 한다면, 이 디렉토리 아래의 customize-image.sh 파일을 수정하면 된다. 이와 관련한 보다 자세한 사항은 아래 site에서 확인 가능하다.

3.6 tftpboot & NFS booting하기
NFS booting 시험을 위해, u-boot prompt 상에서 몇가지 명령을 확인해 보았는데, 어쩐 일인지 network(ethernet) 관련 명령이 모두 빠져 있다(ping, tftpboot 등의 명령이 안보임). 따라서 tftpboot로 kernel image 등을 RAM에 loading 방법이 없으므로 tftpboot & NFS booting이 현재로써는 불가능하다. 그렇다면 아래 4.2절의 FEL mode를 이용한 부팅이 이를 대신할 수 있는지 눈여겨 보도록 하자.


4. Allwinner A64의 이해 1: boot sequence & FEL mode 소개
이번 절에서는 PINE A64(+) 보드의 부팅 흐름에 관하여 살펴 보고자 한다. Allwinner는 다 좋은데, 문서가 좀 빈약한 편이다. 언제나 결정적인 내용이 없다는 점이 아쉽다. :(

4.1 A64 SoC Boot sequence(legacy 방법)
a) A64 SoC은 다른 Allwinner SoC 처럼, BROM code(0 번지에서 시작함)를 실행하는 것으로 부터 booting을 시작한다.
   => BROM code는 32-bit ARM code이며, SoC 내에 있어 일반 user가 건드릴 수 없는 영역임.

b) SD card를 꽂지 않은 상태로 전원을 인가하게 되면 FEL mode로 진입하게 된다. 
   => FEL mode는 보통 recovery 용으로 활용된다. 즉, 부팅이 안될 경우, flash memory에 image를 다시 write한다던지 ...

c) 만일 FEL mode 조건에 해당하지 않을 경우라면, SD card의 sector 16(8Kbyte)으로 부터 32KB 만큼을 읽어서 SRAM에 올린 후, 이를 실행시킨다.
   => 이 코드(boot0라고 부름) 역시 여전히 32-bit ARM code임.

d) 이제 부터는 boo0가 실행되면서 DRAM controller를 초기화시킨 후, 아래의 세가지 일을 수행한다.
  d-1) SD card의 38192 (19096 KByte) sector로 부터 u-boot code를 읽어 DRAM(아마도 0x4a000000)에 적재한다. 
  d-2) 다음으로 SD card의 sector 39664로 부터  19832 KByte를 읽어들여, BL3-1이라고 부르는 ATF(ARM Trusted Firmware) image를 DRAM(0x40000000)에 올린다.
  d-3) 마지막으로 39752 (19876 KByte) 섹터로부터  arisc management core 관련 코드(SCP firmware)를 읽어 SRAM(0x40000) 올려 준다.
이어서 RMR write를 통해 warm-reset을 하게 되는데, 이 과정에서 RVBAR 레지스터(Reset Vector Base Address Register, EL3)에 주솟값을 넣어 둠으로써 ATF entry point로 jump하게 된다.

e) ATF는 non-secure 실행을 위해 boot core를 초기화하고, u-boot을 실행하기 위해 non-secure AArch32 EL1으로 전환된다.

f) 이후 u-boot이 실행되게 된다(32-bit mode)

g) 마지막으로 u-boot을 통해 linux kernel이 구동된다.
   => u-boot은 kernel을 시작하기 전에 ATF의 smc 서비스 callback을 이용하게 되는데, 이 코드는 AArch64 non-secure EL1(이번에는 u-boot이 아니라 kernel entry point)을 return해 주므로써 kernel을 구동시키게 된다.

참고 사항 1: SCP firmware는 ARM core하고는 독립적으로 PMIC를 제어하는 용도로 사용된다.
참고 사항 2: ATF image는 PSCI firmware의 runtime part를 포함하고 있으며, AArch64 EL3 mode로 구동된다.

위의 부팅 흐름을 간략히 정리해 보면 다음과 같다.

<A64 boot sequence>
BROM(32-bit) in SoC
  |
  +---> boot0(32-bit)
            |
            +---> load u-boot, ATF(= BL3-1, 32-bit), SCP firmware
                       |
                       +---> ATF(= BL3-1)
                                  |
                                  +---> u-boot(32-bit)
                                             |
                                            +---> kernel(64-bit)

보다 자세한 사항(원문)은 아래 site의 내용을 참조하기 바란다.

위의 boot sequence 관련 내용 설명 중 등장한 EL1, EL3 등과 관련해서는 아래 내용을 확인할 필요가 있어 보인다.

<AArch64 exception model>
  EL0 : Applications.
  EL1 : OS kernel and associated functions that are typically described as privileged.
  EL2 : Hypervisor.
  EL3 : Secure monitor.

그림 4.1 AARCH64 Exception model(출처: 참고문헌 [5]에서 발췌)

<TBD> AArch64 memory layout
그림 4.2 AARCH64 MMU 지원(Virtual address space)(출처: 참고문헌 [5]에서 발췌)

참고 사항: AARCH64 아키텍쳐에 관해서는 추후 좀 더 연구해 보는 것으로 하자.

4.2 FEL mode(boot over USB OTG)
Allwinner SoC은 USB OTG를 통해 부팅 가능한 FEL 모드라는 것을 지원한다. Android의 fastboot 기능과 유사한 기능이라고 볼 수도 있는데, FEL 기능 자체가 BootROM(primary bootloader) 내에 탑재되어 있으므로, fastboot(일반적으로 secondary bootloader에 탑재) 방식이 할 수 없는 일 즉, boot media(예: NAND flash)에 설치된 image(혹은 secondary bootloader)에 이상이 생겼을 경우, 이를 복구하는 것이 근본적으로 가능한 방식이라 하겠다(JTAG이 없이도 FEL mode만으로 복구가 가능함. 물론 JTAG은 BootROM 영역도 제어가 가능한 방식이지만 ...).

FEL mode와 관련해서는 아래 두 site의 내용을 참고하기 바란다.

a) FEL mode로 진입하
FEL mode 시험을 위해서는 보드가 FEL mode로 진입해야만 하며, 이를 확인하기 위해서는 sunxi-fel이라는 도구가 필요하다.

<FEL mode 진입 방법>
- Target board에서 microSD를 제거한다.
- Host PC와 target board 간을 USB-A male to A male 케이블을 이용하여 연결한다(반드시 2개의 USB port 중 위 port에 연결해야 함 - 그림 4.3 참조)
- 이 상태에서 전원을 인가한다.

그림 4.3 USB-A male to USB-A male 케이블 연결 모습
참고 사항: 2개의 USB(A type) port 중, 위의 port에만 FEL mode가 동작 가능하도록 회로 설계되어 있다.

$ lsusb
  => 먼저 lsusb 명령을 실행하여 usb OTG 장치(target board)가 제대로 인식되는지 확인해 본다.
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
Bus 001 Device 004: ID 148f:5372 Ralink Technology, Corp. RT5372 Wireless Adapter
Bus 001 Device 003: ID 2188:0ae1  
Bus 001 Device 002: ID 03f0:044a Hewlett-Packard 
Bus 001 Device 007: ID 1f3a:efe8 Onda (unverified) V972 tablet in flashing mode
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

다음으로 sunxi-fel tool을 설치(source를 내려 받아 build)해 보도록 하자.
sudo apt-get update
sudo apt-get install -y make gcc pkg-config libusb-1.0-0-dev
  => 무엇 보다도 libusb-1.0-0-dev package가 필요하다.
$ mkdir sunxi_tools
$ cd sunxi_tools/
git clone https://github.com/linux-sunxi/sunxi-tools
$ cd sunxi-tools/
make; sudo make install

$ sudo sunxi-fel version
  => 아래와 같은 메시지가 출력되면 정상적으로 fel mode로 진입한 것임.
AWUSBFEX soc=00001689(A64) 00000001 ver=0001 44 08 scratchpad=00017e00 00000000 00000000

sunxi-fel tool을 사용하면 아래와 같이 BROM image(binary) 추출도 가능하다.
$ sudo sunxi-fel hexdump 0x0000 128
$ sudo sunxi-fel hexdump 0x2C00 128
  => eGON 이라는 string이 보이는가 ?
그림 4.4 sunxi-fel tool을 이용하여 BROM 이미지 추출

그렇다면, 이제 부터는 FEL mode로 부팅하는 방법을 검토해 보기로 하자.
b) FEL mode로 부팅하기(legacy 방법)
  => longsleep 및 armbian(default version) 모두 kernel 3.10.105를 사용하고 있음. 아래 내용은 이 version을 기준으로 정리한 것임.

정상 부팅 시 BROM이 제일 먼저 구동시키는 것은 boot0 loader이다. 2.3절의 longsleep의 디렉토리를 뒤져 보면 boot0.bin 파일을 찾을 수 있는데, sunxi-fel을 써서 boot0.bin loader를 구동시켜 보도록 하겠다.

              [target board]                                       [Ubuntu PC]
                 USB-A port   -------------------------------- USB-A port
              FEL mode 진입                                      sunxi-fel spl boot0.bin
                   BROM                                                       |
                        |                                                            |
                        <------------------------------------------------+
                                          boot0.bin 전달
                        |
                       V
                   Execute boot0.bin by BROM
                       ...

$ sudo sunxi-fel spl ./boot0.bin
  => 위의 관계에 의해 FEL mode에서 실제로 아래와 같이 boot0.bin이 실행됨을 확인할 수 있다.

그림 4.5 sunxi-fel을 사용하여 boot0.bin을 구동시킨 모습

boot0.bin이 구동되는 것을 확인하였으니, 전체 부팅을 하려면 어찌해야 할까 ?
먼저 boot0img라는 프로그램을 이용하여 boot0.bin, scp.bin, bl31.bin, u-boot.bin 및 dt.dtb를 하나의 파일(u-boot-with-dtb.bin)로 통합시켜 보자.
$ boot0img -B boot0.bin -s blobs/scp.bin -d arm-trusted-firmware/build/sun50iw1p1/debug/bl31.bin -u u-boot-pine64/u-boot.bin -e -F dt.dtb  -o u-boot-with-dtb.bin
  => 이 내용은 코드 3.1을 토대로 추출한 것임.

다음으로, sunxi-fel 명령을 사용하여 아래와 같이 FEL 부팅을 시도해 보자. 
$ sudo sunxi-fel spl ./u-boot-with-dtb.bin write 0x42000000 ./Image write 0x43000000 ./pine64.dtb write 0x43300000 ./uInitrd write 0x43100000 ./boot.scr
  => 편의상 관련 binary를 한 곳에 모은 후, 위 명령을 수행하였음(자세한 과정은 생략).
참고 사항: sudo sunxi-fel 명령을 실행하면 sunxi-fel 관련 자세한 사용법을 확인할 수 있다.

위의 명령 실행 결과, 그림 4.5와 동일한 결과가 출력될 뿐, 더 이상 다음 단계(u-boot)로 넘어가지 않고 부팅이 중단되어 버리는 문제가 발생한다. 아무래도 boot0.bin에서 sdmmc를 초기화하려고 하는 부분에 문제(초기화에 실패하더라도 다음 단계로 넘어갔으면 하는데 ...)가 있어 보인다.

아래 site의 내용을 보니, mainline kernel을 사용할 경우, 개선된 다른 부팅 방법이 있는 듯하다. 그렇다면 이 방법을 시도해 보도록 하자.

c) FEL mode로 부팅하기(mainline 방법)
Mainline kernel(4.11.8 <= armbian dev version)을 사용할 경우 FEL mode 부팅이 가능하다.

먼저 armbian에서 compile.sh을 실행한 상태에서 아래 그림 4.6과 같이 kernel version을 Development version으로 변경해 준 후, 아래 작업을 시도해 보자.

그림 4.6 armbian - dev kernel 선택 모습

<u-boot SPL 32bit compilation>
$ sudo make clean
$ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun50i_spl32_defconfig $ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
=> sunxi-spl.bin 파일(32bit)을 생성한다.
=> legacy 방법에서 사용한 boot0.bin을 이 SPL 파일이 대신하게 됨.
참고 사항: SPL은 second stage program loader를 말한다. 이 경우, brom(boot rom)은 first stage program loader에 해당하며, u-boot(64bit)은 TPL(third stage program loader)이라 말할 수 있다.

<normal u-boot 64bit compilation>
$ sudo make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- pine64_plus_defconfig $ sudo make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu-
=> u-boot-dtb.bin 파일(64bit)을 생성한다.
참고 사항: ARCH=arm64가 아니라 arm인 점에 주의해야 한다. u-boot code는 arch/arm64 디렉토리가 없기 때문이다.

sunxi-spl.bin과 u-boot-dtb.bin이 준비되었으니, 아래 코드 4.1의 주소 정보를 참조하여 아래와 같이 sunxi-fel 명령을 실행해 보도록 하자.


코드 4.1 u-boot/include/configs/unxi-common.h 내용 중 일부

<ARM64>
sudo sunxi-fel -v  -p spl sunxi-spl.bin write 0x4a000000 u-boot-dtb.bin write 0x44000 bl31.bin write 0x40080000 ./Image write 0x4FA00000 ./pine64.dtb reset64 0x44000
  => 앞서와 마찬가지로, 관련 파일을 한 곳으로 모아 놓은 상태에서 명령을 실행하도록 하자.
  => 아래 그림 4.8에서 보듯이 u-boot이 구동되는 것을 확인할 수 있다.

그림 4.7 FEL mode로 부팅하기(1) - host PC

그림 4.8 FEL mode로 부팅하기(2) - target board

그림 4.8에서 보듯이 더 이상 진행이 안되므로, u-boot이 구동된 상태에서 <Enter>를 입력하여 u-boot prompt 상태로 진입한 후, 다음 명령을 입력하여 kernel을 구동시켜 보자.
=> booti 0x40080000 - 0x4FA00000
  => 아래와 같이 kernel이 올라오기 시작한다. 단, (아래에는 표시되지 않았지만)kernel message가 정상이 아니다. 뭔가 잘못되었다. 정확한 원인을 찾아봐야 겠다<TBD>.
참고 사항: '-'로 표시된 부분은 ramdisk 자리로, 위에서 설정한 Initrd가 문제가 있어 일단 ramdisk를 제외한 상태에서 부팅하도록 하였다.
그림 4.9 FEL mode로 kernel 부팅하기


5. Allwinner A64의 이해 2: u-boot 부팅 흐름 분석
이번 절에서는 A64 chip을 위한 u-boot bootloader의 대략적인 코드 흐름을 분석해 보도록 하겠다. Bootloader는 보드 bring-up 단계에서 반드시 넘어야 할 산인 만큼, 코드 흐름을 정확히 파악하는 것은 매우 중요하다고 볼 수 있다(그래야 원하는 부분을 제대로 수정할 수 있다).

<Bootloader의 역할>
  - RAM을 초기화한다.
  - device tree를 setup한다.
  - (선택적인 작업) 필요하다면 kernel image(예: Image.gz)의 압축을 해제한다.
  - kernel image를 call한다.

5.1 u-boot 부팅 flow 분석 1(legacy u-boot)
아래 코드는 legacy u-boot code(2014.7)의 부팅 흐름을 정리해 본 것이다. 단순히 코드를 따라가면서 정리해 본 것이기 때문에, 내용 중 일부는 사실과 다를 수도 있다. 주의를 요한다.

코드 5.1 legacy u-boot code 흐름

5.2 u-boot 부팅 flow 분석 2(mainline u-boot)
아래 코드는 mainline u-boot (2017.01-rc1)을 기준으로 부팅 흐름을 재 정리한 것이다. 코드 5.1과 비교해 볼 때, 전체적인 코드 흐름은 대동소이하다고 볼 수 있으나, 주요 파일의 위치에 있어서는 많은 차이가 있는 것을 알 수 있다. 역시 내용 중 오류가 있을 수 있음을 밝힌다.

코드 5.2 mainline u-boot code 흐름

아래 그림은 arch/arm/lib/crt0_64.S에 있는 _main code(함수)의 전체 흐름에 관한 설명 부분이 되겠다. 위의 코드 5.2와 더불어 읽어 보면 좋을 듯 보인다.

그림 5.1 arch/arm/lib/crt0_64.S _main 코드 흐름 요약

5.3 run bootcmd 코드 분석(legacy code)
지금부터는 u-boot에서 run bootcmd 명령이 실행될 때 벌어지게 되는 코드 흐름을 분석해 보고자 한다. 먼저 아래 그림 5.2는 printenv로 확인한 u-boot 명령을 화면 capture한 것이다. 아래 내용에 의하면 run bootcmd 명령은 다시 run mmcbootcmd 명령을 실행하고 있음을 알 수 있다.

그림 5.2 u-boot printenv 내용

(u-boot 명령이 매우 긴 관계로)위의 그림 5.2의 우측 내용이 짤려 있으므로, 이 내용을 u-boot code include/configs/sun50iw1p1.h에서 확인해 보기로 하자.


      
코드 5.3 include/configs/sun50iw1p1.h 내용 중 run mmcbootcmd 명령 관련

코드 5.3의 내용이 너무 복잡해 보이니, 이를 좀 더 알아보기 쉽게 단순화시켜 정리해 보면 다음과 같다.

그림 5.3 run mmcbootcmd 명령 실행 흐름 정리

위의 그림 5.3의 내용 중, 가장 마지막에 위치한 booti 명령이 최종적으로 kernel을 구동시키는 부분이라고 볼 수 있는데, 실제 코드 흐름(kernel loading 과정)을 대략적으로 정리해 보면 다음과 같다.

코드 5.4 booti 명령을 수행하는 코드 흐름

마지막으로, ARM64 kernel image(Image) 파일의 header 정보 및 MAGIC key 값을 소개하는 것으로 이번 절을 마무리하고자 한다.

코드 5.5 ARM 64 kernel Image header 및 magic 값(common/cmd_bootm.c)

<TBD> mainline u-boot에서의 run bootcmd 명령 흐름 분석


6. Allwinner A64의 이해 3: kernel 초기화 코드 분석
이 절에서는 bootloader(u-boot)에 의해 kernel이 loading되어 실행된 이후 arm64 machine(PINE A64 board)이 초기화되기 까지의 과정을 분석해 보고자 한다.

AARCH64용 kernel startup code를 분석하는 일은 간단한 일이 아니다. 그 이유는 AARCH64의 architecture에 대한 충분한 이해(참고 문헌 [1] 참조)가 선행되어야 하기 때문이다. 따라서 이 절에서는 kernel startup code(arch/arm64/kernel/head.S)를 상세히 분석하는 것 보다는 system이 reset된 이후, 대략적으로 어떠한 과정을 거쳐 device tree를 준비하고, 주요 device driver를 초기화하는지에 촛점을 맞추어 내용 전개를 진행하도록 하겠다.

참고로, 아래 분석 내용은 legacy kernel 3.10.107을 기준으로 정리한 것임을 밝힌다(따라서 mainline kernel의 경우 차이가 날 수 있겠음)

6.1 cpu0에서의 대략적인 부팅 흐름
먼저 cpu0의 부팅 과정(코드 6.1 ~ 6.6)을 대략적으로 정리해 보면 다음과 같다.

arch/arm64/kernel/head.S
  |
  +------> start_kernel( ) in init/main.c 
                  |
                  +------> setup_arch( ) in arch/arm64/kernel/setup.c
                                 |
                                 +------> unflatten_device_tree( ) in drivers/of/fdt.c
                                               |
                                               +------> __unflatten_device_tree( ) in drivers/of/fdt.c

Linker script 파일(64-bit arm kernel 의 경우는 arch/arm64/kernel/vmlinux.lds)은 linker가 참조하는 파일로, 각종 object 파일을 어떤 식으로 link하여 kernel image인 vmlinux(ELF 파일 형식) 파일을 생성할지를 결정하는 역할을 한다. 아래 코드 6.1에 의하면 코드의 시작은 ENTRY(_text) 부분이며, 이는 다시 아래 관계(.head.text section)에 의해 코드 6.2(arch/arm64/kernel/head.S)와 연결되는 것을 알 수 있다.

<vmlinux의 시작 지점 파악>
----------------------------------------------------------------------------------
<arch/arm64/kernel/vmlinux.lds>
ENTRY(_text)
...
. = (0xffffffffffffffff << ((39) - 1)) + 0x00080000;        /* RAM에서의 kernel start address */
 .head.text : {
  _text = .;
  *(.head.text)

 }

<arch/arm64/kernel/head.S>
...
__HEAD

<include/linux/init.h>
#define __HEAD      .section    ".head.text","ax"
----------------------------------------------------------------------------------

참고 사항: linker script 관련 보다 자세한 사항은 참고문헌 [16]을 참조하기 바란다.

코드 6.1 arch/arm64/kernel/vmlinux.lds

아래 코드 6.2의 .head.text section에서는 b stext 명령을 실행하여 코드 6.3으로 바로 분기한다.

참고 사항: AArch64에서는 x0 register에 device tree blob(= FDT blob)의 주소가 담겨 있음을 알 수 있다.

코드 6.2  arch/arm64/kernel/head.S 시작 코드

stext 함수 내에서는 bl el2_setup 등 다양한 처리를 진행하게 된다(이후 복잡한 과정에 대한 설명은 생략).

코드 6.3 arch/arm64/kernel/head.S - stext

이어서, (중간 단계의 복잡한 단계를 실행 한 후) __mmap_switched: 로 jump한 코드는 마침내, b start_kernel 명령을 실행하므로써, kernel의 main 함수(여기부터 C code임)로 진입하게 된다.

코드 6.4 arch/arm64/kernel/head.S - start_kernel( ) 함수 호출, cpu0에서 수행

다음으로 init/main.c내에 있는 start_kernel( ) 함수는 다시 여러 kernel 초기화 절차를 진행한 후, setup_arch( )라는 함수를 호출하여, 특정 machine과 관련된 설정을 진행하게 된다.

코드 6.5 init/main.c start_kernel( ) 함수

setup_arch( ) 함수의 내부를 들여다보면, 역시 여러 함수를 호출하고 있음을 알 수 있는데, 이 중 unflatten_device_tree( ) 함수를 통해 RAM에 올라와 있는 device tree blob 내용을 가지고  kernel에서 사용할 device tree를 구성하게 된다.

코드 6.6  arch/arm64/kernel/setup.c - setup_arch( ) 함수 호출


6.2 clock & platform device 생성 흐름
한편, 아래 코드는 위의 booting 흐름과는 별도로 unflatten_device_tree( )에 의해 만들어진 device tree를 토대로 clock이 초기화되고, platform device가 생성되는 과정을 정리한 것이다.
------------------------------------------------------------------------------------------------------------------------------
start_kernel( ) in init/main.c
 |
+--> rest_init( )
         |
         +--> kernel_init( )
                   |
                   +--> kernel_init_freeable( )
                            |
                            +--> do_basic_setup( )
                                     |
                                     +--> do_initcalls( )
                                              |
                                              +--> do_initcall_level( )
                                                       |
                                                       +--> do_one_initcall( )
                                                                |
                                                                +--> arch_initcall_sync( )
                                                                        |
                                                                        +--> arm64_device_init( ) in arch/arm64/kernel/setup.c
                                                                                |
                                                                                +-->
                                                                                    {
                                                                                        of_clk_init( ) in drivers/clk/clk.c
                                                                                        => clock provider 관련하여 device tree를 scan한 후, 초기화를 진행한다.

                                                                                        of_platform_populate( ) in drivers/of/platform.c
                                                                                          => device tree data를 이용하여 platform device를 생성하는 작업을 한다.
                                                                                    }
------------------------------------------------------------------------------------------------------------------------------
참고 사항: clock이 어떻게 초기화되는지를 확인하기 위해서는 of_clk_init( ) 함수를, platform device가 어떻게 생성되는지를 살펴 보려면 of_platform_populate( ) 함수의 내용을 살펴 보면 될 것이다.

6.3 cpu1/2/3에서의 대략적인 부팅 흐름
여기까지는 A64 SoC 내의 4개의 CPU  중, cpu0에서 수행하는 작업을 정리한 것이라면, 아래 소개하는 내용은 나머지 CPU 즉, cpu1/2/3에서 수행하는 내용을 정리한 것이다.
-------------------------------------------------------------------------------------------------------------------------------------------------
userspace "echo 1 > /sys/devices/system/cpu/cpu1/online"
혹은
kernel내 여러 곳(cpu를 살리고자 하는 경우 - hotplug or power saving 조건 등)
  |
  +--> cpu_up( ) in kernel/cpu.c
          |
          +--> _cpu_up( )
                   |
                   +--> __cpu_up( )  in arch/arm64/kernel/smp.c
                            |
                            +--> boot_secondary( )
                                    |
                                    +--> cpu_boot( )
                                            |
                                            +--> sunxi_cpu_boot( ) in arch/arm64/kernel/cpu_ops_sunxi.c
                                                    |
                                                    +-->
                                                        {
                                                          sun50i_set_secondary_entry( )
                                                          sun50i_cpu_power_up( )
                                                           |
                                                          +--> 여기서 secondary cpu가 reset 됨 !
                                                            |
                                                          V
                                                           secondary_entry in arm/arch64/kernel/head.S
                                                            |
                                                           +--> secondary_startup
                                                                   |
                                                                   +--> __secondary_startup 
                                                                        |
                                                                       +--> secondary_start_kernel( ) in arch/arm64/kernel/smp.c
                                                       }
-------------------------------------------------------------------------------------------------------------------------------------------------
지금까지 설명한 내용(a, c 내용)을 하나의 그림으로 정리해 보면 대략 다음과 같다.

그림 6.1 AARCH64 SMP booting 흐름 개요도

참고 사항: 그림 6.1의 (A)는 IPI(Inter-Processor Interrupt)를 표시해 주고 있다.

<TBD> mainline kernel에서의 초기화 코드 분석


7. Allwinner A64의 이해 4: Device Tree 분석

Device tree는 최신 ARM linux kernel & device driver를 이해하는데 있어 매우 중요한 programming 요소가 되었다. Device tree와 관련해서는 이미 여러 차례에 걸쳐 내용 소개를 진행한 바 있으나, device tree 내용 자체가 특정 SoC이나 보드를 기술하고 있는 까닭에 chip이 바뀔 때마다 새롭게 이해해야 하는 너무나도 당연하다 하겠다.

아래 그림은 PINE A64(+) 보드의 block diagram으로 device tree를 이해하기 위해서는 보드의 전체 구조와 보드를 구성하는 각각의 구성 요소를 눈여겨 볼 필요가 있다.

그림 7.1 PINE A64(+) 블록도(출처: 참고문헌 [8]에서 발췌)

다음으로, 아래 내용은 A64 memory mapping 테이블 내용 중 일부를 발췌한 것으로, device tree를 구성하는 각각의 node에 부여된 주솟값의 의미를 파악하고자 할 때 활용할 수 있겠다.

...
그림 7.2 Allwinner A64 memory mapping(출처: 참고문헌 [6]에서 발췌)

또한 아래 그림 7.3과 7.4는 PINE A64 보드의 확장 포트인 Pi-2 bus와 Euler bus의 pinout을 보여주는 것으로, device tree pinmux 설정 시 아래 내용이 도움을 줄 수 있을 것으로 보인다.
그림 7.3 PINE A64 Pi 2 bus pinout(인터넷에서 가져옴)

그림 7.4 PINE A64 Euler bus pinout(인터넷에서 가져옴)


(본론으로 돌아와서) 이번 절에서 소개할 PINE A64(+) board를 위한 device tree 파일은 다음과 같다.

참고로, 아래 분석 내용은 legacy kernel 3.10.107을 기준으로 정리한 것임을 밝힌다.

arch/arm64/boot/dts/sun50iw1p1-pine64-plus.dts

코드 7.1 sun50iw1p1-pine64-plus.dts 파일 시작 부분

우선, 위의 내용 중에서 주목할 부분은 A64가 64bit machine인 점을 감안하여 아래와 같이 address와 size cells의 갯수를 모두 2로 설정(32bit 경우는 1로 표시함)해 주었다는 점이다.
------------------------------------------------------------------------------------
    #address-cells = <0x2>;    /* 64bit - address field */
    #size-cells = <0x2>;          /* 64bit - length field */
------------------------------------------------------------------------------------

따라서 하위 노드에서 reg 속성을 지정할 경우, cell의 크기가 32bit인 점을 감안하여, 아래와 같이 표현해 주어야 한다(물론, 경우에 따라서는 아래와 같은 내용이 여러번 반복될 수도 있다).
------------------------------------------------------------------------------------
    reg = <address_high address_low  length_high length_low>;
------------------------------------------------------------------------------------

7.1 CPU
A64 SoC 내에는 4개의 cpu가 존재하며, 각각을 device tree node로 표현하면 다음과 같다.

코드 7.2 PINE A64 device tree - cpu nodes

------------------------------------------------------------------------------------------------------------
        cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a53", "arm,armv8";
            reg = <0x0 0x0>;  /* cpu의 경우는 address-cells이 2로 설정되어 있고, size-cells은 0으로 설정되어 있음 */
            enable-method = "psci";
            cpufreq_tbl = <0x000639C0 0x00075300 0x0007b0c0 0x00080e80 0x0008ca00 0x000927c0 0x00098580 0x0009e340 0x000a4100 0x000afc80 0x000bb800 0x000c7380 0x000d2f00 0x000dea80 0x000ea600 0x000f6180 0x00101d00 0x0010d880 0x00119400 0x00124f80 0x00148200>;  /* cpu frequency tables - 가변 cpu 주파수 table, cpu0에서만 조정 가능함 */
            clock-latency = <0x1e8480>;
            clock-frequency = <0x3c14dc00>;
            cpu-idle-states = <0x90 0x91 0x92>;
        };
------------------------------------------------------------------------------------------------------------

7.2 Memory
아래 내용은 dram controller(DDR3)를 위한 설정 정보와 memory(DRAM)의 시작 주소 및 크기 정보를 device tree로 표현한 것이다.

코드 7.3 PINE A64 device tree - memory nodes

-------------------------------------------------------------------------------------------
     memory@40000000 {  /* 그림 7.2 참조 - 시작번지 */
        device_type = "memory";
        reg = <0x0 0x41000000 0x0 0x3f000000>;   /* address(2 cells) | length(2 cells) */
    };
-------------------------------------------------------------------------------------------

위의 내용대로라면, memory의 크기가 1008MB(= 0x0 0x3f000000)로 1GB가 채 안되는데, 아무래도 이상(PINE A64(+)는 2GB를 사용함)하다. 그렇다면, Armbian OS image로 부팅한 후, target board 상에서 실제 동작 중인 device tree 정보를 확인해 보면 어떨까 ?

root@pine64:/# dtc -I fs /proc/device-tree
-------------------------------------------------------------------------------------------
        memory@40000000 {
                reg = <0x0 0x41000000 0x0 0x7f000000>;  /* address(0x0|0x41000000), length(0x0|0x7f000000) = 2032MB */
                device_type = "memory";
        };
-------------------------------------------------------------------------------------------
정확하지는 않지만, 대략 2GB의 크기가 나온다. 그렇다면 위의 코드 7.3의 내용이 잘못되었다는 얘긴데... 실제 위의 내용이 PINE A64(+)를 위한 것인지 여부는 추후 재확인해 보도록 하겠다.

7.3 System Bus
아래 그림은 SoC의 내의 bus 구조를 표현한 것인데, 이러한 형태의 bus를 linux kernel에서는 특별히 platform bus라고 칭한다. 아래 코드 7.4는 platform bus에 연결되어 있는 장치 주변 장치 controller를 위한 device tree 내용 중 일부를 보여준다.

그림 7.5 A64 processor system bus tree(출처: 참고문헌 [6]에서 발췌)

코드 7.4 PINE A64 device tree - soc nodes

-------------------------------------------------------------------------------------------
        compatible = "simple-bus";    /* platform bus를 의미함 */
        #address-cells = <0x2>;   /* peripheral controller를 접근하는 주소도 64bit인 모양 */
        #size-cells = <0x2>;
        ranges;
        device_type = "soc";

        pinctrl@01f02c00 {
                 compatible = "allwinner,sun50i-r-pinctrl";
                 reg = <0x0 0x1f02c00 0x0 0x400>;    /* address | length */
                 ...
-------------------------------------------------------------------------------------------

7.4 Clock과 PLL
Clock은 cpu는 물론이고 대부분의 peripheral 들 간에 동기를 맞추기 위한 아주 중요한 목적으로 사용되는 만큼, S/W 적으로도 처리해야 할 일이 꽤나 복잡한 녀석이다. 아래 그림은 A64 chip을 구성하는 clock과 PLL의 관계를 표현한 것이며, 코드 7.5는 clock을 device tree로 표현한 내용 중 일부를 보여준다.

그림 7.6 A64 processor bus clock tree(출처: 참고문헌 [6]에서 발췌)


그림 7.7 A64 processor module clock diagram(출처: 참고문헌 [6]에서 발췌)

코드 7.5 PINE A64 device tree - clock nodes

clock & device tree와 관련해서는 아래 site를 참조해 볼 것을 권한다(코드를 아주 상세히 분석하고 있음).


<여기서 잠깐>---------------------------------------------------------------------------------------------------------------
■ PLL 이란 ?
PLL이 무엇인지에 관해서는 아래 site를 참조하기 바란다.

그림 7.8 PLL 장치 개요

■ Clock Gating 이란 ?
"Clock Gating이란, Clock 공급 Gate를 통제함으로써 낭비되는 전력을 최소화 하는 기술이다. 정확한 설명은 CPU 내부를 작은 기능에 따라 작은 Block 단위로 묶고 사용하지 않는 Block에는 Clock을 공급하지 않는 방식이다. 예를 들면, Camera Module을 사용하지 않는 상황이라면 Camera Module과 관련된 CPU Block의 Clock 공급을 제거함으로써 사용하지 않는 CPU Block에서 발생하는 전력 낭비가 없어져 저전력을 구현할 수 있게 되는 것이다. 그리고 Clock Gating을 하면 부수적으로 해당 부분의 발열까지도 감소시켜 한 번에 두 가지 이익을 가져오게 된다. 단, Clock Gating을 사용하기 위해서는 CPU 내부 혹은 SoC( System-on-Chip )가 구조적으로 Clock Gating을 지원해야 한다."
출처: http://m.blog.naver.com/dong880510/140156794477

그림 7.9 Clock gating 개념도(인터넷에서 발췌 - site는 기억나지 않음)
-----------------------------------------------------------------------------------------------------------------------------------

7.5 PMIC(AXP803)
PMIC(power management IC)는 글자 그대로 CPU(정확히는 SoC)의 power를 관리(안정적으로 유지, 충전 기능도 포함)해 주는 역할을 한다. PINE A64 보드에서는 AXP803라는 PMIC를 사용하고 있는데, AXP803 내부에는 아주 많은 regulator(LDO, DCDC converter 등)가 포함되어 있고, S/W 적으로는 이를 적절히 제어(보통 I2C 사용)해 주어야 한다. AXP803 관련 보다 구체적인 사항은 참고문헌[9]를 참조해 주기 바란다.

그림 7.10 AXP803 PMIC block도(출처: 참고문헌 [8]에서 발췌)


코드 7.6 PINE A64 device tree - pmu nodes

코드 7.7 PINE A64 device tree - regulator nodes

참고 사항: 경험상 regulator의 전압 값을 설정하는 부분은 kernel 보다는 그 이전 단계 즉, u-boot에서 작업해 주어야만 제대로 동작하기도 한다.

7.6 Port controller(PIO)
A64 SoC에는 다목적으로 사용되는 input/out pin(PIO) 그룹(port controller라고 함)이 7개가 존재한다. 각 port 그룹은 S/W적으로 제어가 가능(pinmux or pinctrl)한데, 특정 목적(예: UART0, SPI0)으로 해당 pin이 사용되지 않을 경우, (당연히) 이를 GPIO 용으로 사용할 수 있다.

     Port B(PB): 10 input/output port
     Port C(PC): 17 input/output port
     Port D(PD): 25 input/output port
     Port E(PE): 18 input/output port
     Port F(PF): 7 input/output port
     Port G(PG): 14 input/output port
     Port H(PH): 12 input/output port

코드 7.8 PINE A64 device tree - pinctrl

위의 내용 중, uart0@0과 uart1@0 node의 내용만 잠시 살펴 보면 다음과 같다.
---------------------------------------------------------------------------------------------------------------------------------------
            uart0@0 {
                linux,phandle = <0xa2>;
                phandle = <0xa2>;
                allwinner,pins = "PB8", "PB9";  /* 그림 7.4 참조 - Euler bus pinout 29, 30에 해당 */
                allwinner,function = "uart0";
                allwinner,pname = "uart0_tx", "uart0_rx";
                allwinner,muxsel = <0x4>;
                allwinner,pull = <0x1>;
                allwinner,drive = <0xffffffff>;
                allwinner,data = <0xffffffff>;
            };

            uart1@0 {
                linux,phandle = <0xa3>;
                phandle = <0xa3>;
                allwinner,pins = "PG6", "PG7", "PG8", "PG9";  /* 아래 그림 7.11 참조 - BT 연결용으로 사용 */
                allwinner,function = "uart1";
                allwinner,pname = "uart1_tx", "uart1_rx", "uart1_rts", "uart1_cts";
                allwinner,muxsel = <0x2>;
                allwinner,pull = <0x1>;
                allwinner,drive = <0xffffffff>;
                allwinner,data = <0xffffffff>;
            };
---------------------------------------------------------------------------------------------------------------------------------------
그림 7.11 PG6, PG7, PG8, PG9의 용도 - Bluetooth UART 연결용으로 사용(참고문헌 [8]에서 발췌)


시간 관계상 PINE A64 device tree를 대략적으로 살펴 보았으나, 추후 좀 더 세밀하게 다시 정리해야 할 것으로 보인다.


8. 맺음말
이번 blog post에서는 PINE A64 보드의 개발 환경(64bit cross-toolchain, pine64 longsleep의 image 생성 방법, armbian, remix OS)과 booting flow(BROM ~ kernel까지의 과정) 및 device tree에 관해서 대략적으로 살펴 보았다. 일반적으로 embedded linux system을 개발하는 과정은 아래와 같이 3 단계로 나누어 생각해 볼 수 있다.

  1단계: 개발 환경을 갖추고, u-boot code 등을 적절히 수정하여 kernel이 시작되도록 만들기 까지의 단계

  2단계: 각종 device driver가 정상 동작하도록 만드는 단계(완벽한 kernel booting되는지 확인)
    => device driver가 정상적으로 동작하도록 만드는 일은 device tree와 연관성이 높다.

  3단계: 마지막으로 실제로 필요한 application이 제대로 동작되도록 만드는 단계

이러한 기준에 비추어 볼 때, 이번 posting의 내용은 1단계에 해당된다고 볼 수 있다.

언제나 그렇 듯, 처음 계획했던 것에 비해 마무리가 늘 아쉽다. 내용 중 설명이 미진한 부분에 대해서는 추후 좀 더 보강해 볼 것을 기약하며, 이번 blog post를 마치도록 하겠다. 창문 바깥쪽으로 시원한 장마 빗줄기가 내리치고 있다 ~


References
1. ARM Cortex-A53 MPCore Processor(Revision: r0p2) TRM, ARM.
2. https://www.kernel.org/doc/Documentation/arm64/booting.txt
3. https://www.kernel.org/doc/Documentation/arm64/memory.txt
4. https://lwn.net/Articles/505682/
5. Linux on AArch64 ARM 64-bit Architecture, Catalin Marinas, LinuxCon North America 2012.
  => ARM Cortex-A53 & ARRCH64 관련 설명

6. Allwinner_A64_User_Manual_V1.0.pdf
  => A64 chip user manual
7. A64_Datasheet_V1.1.pdf
  => A64 chip datasheet
8. PineA64plus 2GB Rev C-20160113_Release.pdf
  => Pine64 회로도
9. AXP803_Datasheet_V1.0.pdf
  => AXP803 PMIC chip datasheet
10. https://github.com/longsleep/build-pine64-image
  => longsleep's github for pine64 build
11. http://wiki.pine64.org/index.php/Main_Page#Freshman_Page_.5BA.K.A._Quick_Start_Guide.5D
12. https://blog.hypriot.com/post/the-pine-a64-is-about-to-become=the-cheapest-ARM-64-bit-platform-to-run-Docker/

13. www.armbian.com
  => armbian 공식 site

14. http://www.rfdh.com/bas_rf/begin/pll.php3
  => PLL에 대한 개념 설명이 잘 되어 있음.
15. http://jake.dothome.co.kr/clk-1/
  => kernel clock framework 관련 분석 내용이 아주 훌륭함.

16. http://www.bravegnu.org/gnu-eprog/
  => 32bit ARM embedded programming(assembly code, linker script, exceptions  등 설명)

17. https://github.com/apritzel/u-boot/blob/pine64-spl-wip/board/sunxi/README.pine64#L58
  => PINE64 FEL mode 관련(최신 방법 소개)
18. https://linux-sunxi.org/Pine64#Manufacturer_images
  => linux-sunxi site for Pine64

Slowboot