qemu에서 u-boot를 통해 리눅스 커널,디바이스트리,램디스크 이미지를 부팅한다. 리눅스 실행 커맨드로 별도의 루트 파일 시스템을 지정한다.
buildroot를 이용하여 리눅스 커널(zImage), 디바이스트리파일(vexpress-v2p-ca9.dtb), 루트파일시스템(rootfs.ext2)를 생성한다. 이 과정에서 크로스 컴파일러도 만들어지는데 이를 사용하여 u-boot,busybox 빌드에 이용한다.
buildroot를 이용하여 u-boot를 만들 수 있다.하지만 공식적으로 유지보수되는 u-boot의 github에서 빌드할 것이기 때문에, buildroot를 빌드할 때 u-boot는 따로 만들지 않겠다.u-boot 깃허브판을 사용하면 menuconfig에서 옵션을 세밀하게 설정할 수 있다.
git clone https://github.com/buildroot/buildroot.git cd buildroot git checkout 2024.02-rc2 make qemu_arm_vexpress_defconfig make -j$(nproc)
메모
buildroot의 설정 패키지를 확인할 수 있다.
make list-defconfigs
빌드하기 전에 설정을 바꿔서 루트파일시스템의 용량을 늘릴 수도 있다.
BR2_TARGET_ROOTFS_EXT2_SIZE="128M"
빌드가 완료하면 output폴더가 만들어 진다.
./output/images : rootfs.ext2(루트파일시스템), zImage(리눅스커널),dtb(디바이스 트리 블롭 파일), start-qemu.sh(buildroot설정에서 qemu 유효화하였기에 qemu로 실행할 수 있도록 이 스크립트가 생성되었다)
./output/host/bin : prefix가 arm-linux-인 크로스 툴체인의 툴이 이 위치에 생성된다. 크로스 컴파일러를 쓰기 위해 환경변수 PATH에 이 경로를 추가한다.
./output/target : ./output/images/rootfs.ext2이 있기 때문에 ./output/target 폴더를 사용하지 않는다. 하지만, 예상하기로 타겟의 루트파일시스템의 내용물을 ./output/target 폴더 안에 생성하여, rootfs.ext2 이미지를 만든다고 생각한다.
sd card이미지를 만들어 target 폴더 내용물을 sd 이미지에 복사하여 리눅스 부팅이 되는지 확인하였다.이 경우, sd카드 이미지에 target 폴더의 내용을 붙여 넣으면 된다.
타겟에 적합한 u-boot를 빌드한다.
git clone https://github.com/u-boot/u-boot.git cd u-boot git checkout v2024.04 make ARCH=arm CROSS=arm-linux- vexpress_ca9x4_defconfig make -j$(nproc)
빌드가 완료되면 u-boot 파일이 생성된다.
busybox를 이용하여 initramfs를 만든다.
git clone https://github.com/mirror/busybox.git cd busybox git checkout 1_36_stable make ARCH=arm CROSS=arm-linux- defconfig make ARCH=arm CROSS=arm-linux- menuconfig Settings ---> [*] Build static binary (no shared libs) make ARCH=arm CROSS=arm-linux- make ARCH=arm CROSS=arm-linux- install
빌드가 완성되면 _install 폴더가 생성된다.
_install 폴더 안에 initramfs에 필요한 파일을 만든다.
cd _install
mkdir ./{dev,proc,sys}
mkdir -p ./mnt/newroot
initramfs.cpio에 파일시스템을 구축한다.압축파일을 만들고, u-boot에 로딩할 수 있도록 uRamdisk로 변환한다.
find . | cpio -H newc -ov --owner root:root -F ../initramfs.cpio cd ../ gzip initramfs.cpio mkimage -A arm -O linux -T ramdisk -d initramfs.cpio.gz uRamdisk
u-boot에서 리눅스 부팅하기 때문에 qemu는 u-boot 실행과 필요 파일의 로딩이 필요하다.
u-boot에서 리눅스 부팅하기 위해 필요한 요소(리눅스 커널 이미지, device tree blob, initramfs)를 qemu에서 직접 로딩하는 방법과 해당 파일을 넣은 sd card 이미지를 연결하는 방법이 있다.
리눅스를 initramfs가 아닌 buildroot에서 만든 루트시스템를 이용하므로, qemu에서 해당 디바이스를 연결한다.
sd카드 이미지에 2개의 파티션을 만들어, 1번째 파티션에 u-boot에서 사용할 파일(리눅스, dtb, initramfs)을 넣고, 2번째 파티션에 리눅스 파일시스템을 넣는다면, qemu에서 sd카드 디바이스 연결하고 u-boot 안에서 메모리에 로드하면 된다.
리눅스, dtb, initramfs를 메모리에 로드하는 방법은 qemu에서 하는 방법과 u-boot에서 하는 방법이 있다.
#!/bin/sh export QEMU_AUDIO_DRV="none" qemu-system-arm \ -M vexpress-a9 -smp 1 -m 128 \ -net nic,model=lan9118 \ -net user \ -kernel u-boot \ -nographic \ -drive file=rootfs.ext2,format=raw,if=sd \ -device loader,file=zImage,addr=0x62000000 \ -device loader,file=vexpress-v2p-ca9.dtb,addr=0x63000000 \ -device loader,file=uRamdisk,addr=0x63008000
이로써, u-boot가 부팅되면 리눅스 부팅 명령어를 설정하고 부트한다.
setenv bootargs "console=ttyAMA0,115200 rootwait root=/dev/mmcblk0" bootz 0x62000000 0x63008000 0x63000000
u-boot는 초기화 동작을 정의할 수 있는 스트립트를 만들 수 있다.
u-boot가 시작할 때 bootcmd라는 환경변수에 기술된 명령어를 수행한다.
u-boot가 동작하고, 3초라는 시간내에 입력이 없으면, bootcmd의 명령를 자동으로 수행한다.
bootcmd에 스크립트 파일을 로드하여 실행하는 방법을 지정하면, boot.scr을 불러와 실행할 수도 있다.
bootargs, bootcmd를 수정하여 위에서 수행한 명령어를 실행할 수 있도록 했다.
saveenv를 입력하여 환경변수를 저장하면, u-boot를 재실행하면 자동으로 리눅스 부팅까지 이어진다.
https://ichiri.biz/tech/embedded-linux-beaglebone-black-3-boot-scr/#toc5