2023년 연말을 최대한 유의미하게 보내고자 아래와 같이 목표를 세우고 달성하고자 노력하였다.
목표:
aarch64 시스템을 최소 설정으로 OS없이 동작하도록 초기화.
- System 동작 확인
- GIC를 사용한 (SPI, PPI) 설정법 확인
- UART를 사용한 입출력 - interrupt로 처리(SPI)
- timer interrupt처리 (PPI)
대부분의 경우 새로운 BSP는 기존 BSP를 수정하여 만들고, u-boot등 기성 boot loader를 사용하기 때문에 주의 깊게 살펴볼 기회가 없었던 aarch64 시스템 초기화 방법 및 GIC 사용법을 정리하고자 한다.
준비:
( ubuntu or WSL/Windows )
1. cross-compiler설치
$ sudo apt-get update
$ sudo apt-get install gcc-aarch64-linux-gnu
2. qemu설치
$ sudo apt-get install qemu-system-aarch64
우선 테스트를 위한 HW를 선정해야 한다. rpi4 등 실제 HW에서도 진행할 수 있지만, qemu환경을 사용하면, 실제 HW없이 구현 및 테스트가 가능하므로 일단 qemu의 aarch64환경에서 진행하기로 결정하였다.
qemu는 여러가지 HW의 emulation을 지원하는데, 그중 "virt" machine을 선정하였다.
‘virt’ generic virtual platform (virt) — QEMU documentation
- 2023년 말 기준 qemu정식 버전은 rpi4를 지원하지 않지만, rpi4 emulation을 지원하는 qemu를 쉽게 구할 수 있었고, 빌드도 쉽게 가능하였지만 일단 "virt"에서 먼저 구현하기로 하였다. rpi는 ARM의 GIC interrupt controller가 아닌 다른 interrupt controller를 사용하기 때문에 study의 목적에 부합하지 못하였다.
- qemu virt machine은 GICv2를 지원하지만, 옵션을 사용하여 GICv3를 선택할 수 도 있다. 먼저 GICv2에서 동작을 확인한 후 GICv3까지 동작시키는 것을 목표로 하였다.
aarch64 부팅 과정 고찰:
aarch64는 아래와 같은 복잡한 부팅 과정을 가진다. 이 과정을 이해해야 어떤 부분부터 직접 작성할지 결정할 수 있었다.
BL1 -> BL2 -> BL31 ( Secure Monitor / ATF )
-> BL32 ( OP-TEE OS )
-> BL33 ( u-boot & OS)
BL1 : 1st stage boot loader
BL2 : 2ed stage boot loader
BL3x: 3rd stage boot loader
BL31 : EL3 -runtime f/w (Secure monitor)
BL32 : Secure EL1 f/w : TEE OS
BL33 : Non-Secure EL1 Non-trusted f/w ( u-boot etc)
최종적으로 일반 OS를 구동 시키고자 한다면, BL33에 해당하는 부분을 작성하는 것을 목표로 잡을 수 있었다. 즉 기존의 u-boot를 대신하여 실행할 수 있는 f/w작성하고자 한다.
qemu virt machine을 사용해서 일단 u-boot를 실행해 보자.
$ qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel u-boot
위에서 "u-boot"는 elf 형식의 u-boot 파일명이다.
-kernel을 사용할 경우 BL1/BL2/BL31/BL32가 사용되지 않았다. BL33(여기서는 u-boot)만 사용하여 동작이 가능하도록 qemu가 emulation해준다. 따라서 다른 BL을 신경 쓰지 않고 작업 진행이 가능하다.
-boot 옵션을 사용하여 아래와 같이 실행도 가능하다.
$ qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -bios u-boot.bin
==> EL3 없음. Secure World 비활성화됨.
$ qemu-system-aarch64 -M virt,secure=on -cpu cortex-a53 -bios u-boot.bin -nographic
==> u-boot에서 EL3 접근이 가능한 상태로 실행 됨. 이 옵션이 없으면 QEMU는 EL2, EL1만 실행 가능함.
만약 굳이 BL1, BL3, BL31, BL32, BL33을 사용해서 동작 시킨다면 아래와 같이 할 수 있다.
$ qemu-system-aarch64 -M virt,secure=on -smp 2 -m 4G -bios bl1.bin -d unimp -semihosting-config enable=on -nographic
단 이때 실행 디렉토리에 bl1.bin, bl2.bin, bl31.bin(Tee OS) bl33.bin(u-boot.bin)이 있어야 한다. 또한 semihosting기능은 EL1, EL2에서는 기본적으로 enable되어 있지만, EL3에서는 보안상의 이유로 기본 값이 disable이므로 명시적으로 enable하여야 동작(로그출력)이 가능하다.
bl1.bin, bl2.bin, bl31.bin은 아래에서 구할 수 있었다.
GitHub - ARM-software/arm-trusted-firmware: Read-only mirror of Trusted Firmware-A
bl33.bin은 u-boot.bin의 symbolic link로 만들었다.
위 링크의 BL2는 BL31대신 BL33(u-boot)를 로드한다.
BL1->BL2->BL33(u-boot), 즉 이 경우 실제로 BL31(Secure Monitor)와 BL32(TEE OS)는 로드 되지 않는다. 일단 Secure Monitor와 TEE OS는 사용하지 않기로 한다.
Bl1, BL2, BL31/BL32등의 개발에 관심이 있는 것이 아니라면 굳이 secure=on을 사용하여 EL3를 사용할 이유는 없다. (일반적으로 HW 업체에서 자신들의 CPU에 맞는 BL1를 제공한다). 따라서 이후에서는 virtualization=on을 사용하여 EL2에서 시스템을 시작 시키며, u-boot 대신 직접 작성하는 바이너리를 올려서 진행하기로 하였다.
빌드
aarch64-linux-gnu-as -march=armv8-a -mcpu=cortex-a72 -g -c ...
aarch64-linux-gnu-gcc -DGICV3 -march=armv8-a -mcpu=cortex-a72 -g -I. -fno-stack-protector -mgeneral-regs-only -c ...
aarch64-linux-gnu-ld -n ... -T virt.ld -o my_bl33.elf
개발 중 unexpected sync. exception이 발생 하는 문제가 있었다. 다행이 최근에 회사 동료가 동일한 문제를 해결한 적이 있어 해결책을 알고 있었다. -mgeneral-regs-only 추가.
- 계속 -
댓글 없음:
댓글 쓰기