2023년 12월 31일 일요일

AARCH64 bare metal 로 동작 시키기 - 1

2023년 연말을 최대한 유의미하게 보내고자 아래와 같이 목표를 세우고 달성하고자 노력하였다. 

목표: 

aarch64 시스템을 최소 설정으로 OS없이 동작하도록 초기화. 

  1. System 동작 확인
  2. GIC를 사용한 (SPI, PPI) 설정법 확인
  3. UART를 사용한 입출력 - interrupt로 처리(SPI)
  4. 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

                -> BL32

                -> BL33

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,virtualization=on -m 4G -smp 2 -kernel u-boot -nographic


위에서 "u-boot"는 elf 형식의 u-boot 파일명이다. 

이 경우  BL1/BL2/BL31/BL32가 사용되지 않았다. BL33(여기서는 u-boot)만 사용하여 동작이 가능하도록 qemu가 emulation해준다.  따라서 다른 BL을 신경 쓰지 않고 작업 진행이 가능하다. 


만약 굳이 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 bl33.bin이 있어야 한다. 

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로 만들었다. 

실제 하드웨어와 유사하게 u-boot를 실행한 상태에서 내가 작성한 프로그램을 메모리에 로드하여 go addr 명령으로 실행하는 것이 가능하지만, 굳이 그렇게 할 필요는 없다.  또한 bl1이나 bl2, bl31등의 개발에 관심이 있는 것이 아니라면 굳이 secure=on을 사용하여 EL3를 사용할 이유도 없다 (일반적으로 HW 업체에서 자신들의 CPU에 맞는 BL파일들을 제공한다).  따라서 이후에서는 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 추가. 


- 계속 -




댓글 없음: