IT_Study/Embedded System

Operating System (16) : Bootloader, Booting Process, U-boot를 이용한 custom command 생성

__Vivacé__ 2023. 4. 25. 17:45

하드웨어 제어에서, Interrupt의 중요성

키보드, 마우스, 네트워크 통신 등 이 모든 것은 바로 인터럽트이다.

 

인터럽트가 발생하면,

해당 인터럽트가 발생 시 호출되는 함수가 돌아와진다 -> 콜백 함수

 

펌웨어에서의 인터럽트 발생과 리눅스(OS)에서의 인터럽트 발생은 차이가 있다.

펌웨어에서의 인터럽트 발생
 - 펌웨어는 하드웨어에 가까운 소프트웨어로, 하드웨어를 초기화하고, 특정 하드웨어 기능을 제어하는 역할
 - 펌웨어에서 인터럽트가 발생하면, 펌웨어 내부의 인터럽트 핸들러가 직접 인터럽트를 처리
 - 이 경우, 인터럽트 처리는 매우 간단하며, 빠른 처리 속도가 중요합니다.

리눅스(운영체제)에서의 인터럽트 발생
 - 리눅스와 같은 운영체제에서 인터럽트가 발생하면, 커널에서 인터럽트를 처리
 - 커널은 인터럽트 발생시, 해당 인터럽트에 대한 처리 루틴을 찾고, 인터럽트 처리를 위한 커널 루틴을 실행
 - 이 과정에서 운영체제는 인터럽트 처리를 위해 리소스 할당, 스케줄링, 동기화 등과 같은 고급 기능을 사용하여 처리

 

타이머

delay() vs timer()

delay()는 정확한 시간 측정이 어렵다 -> 커널이 멈추기 때문

 

timer() : CPU의 클럭 수를 이용해서 시간을 측정 


Bootloader

운영체제를 실행하기 전에 메모리에 올려지는 프로그램을 의미

하드웨어 초기화, 운영체제 로딩 등을 담당

기존의 Device Driver를 개발 시, app & device file & H/W 등이 필요하지만,

부트로더에서는 H/W랑 echo.c를 수정한 test code만 있으면 OS 진입전에 H/W 접근이 가능해서 빠른 테스트가 가능하다.

 

 

Image

특정 프로세스를 실행하기 위한 모든 파일과 설정 값을 지닌 것으로, 더 이상의 의존성 파일을 컴파일하거나 이것저것 설치할 필요 없는 상태 바이너리 파일을 의미

 

리눅스 커널 이미지 파일은 "/boot"에 있다

하단 빨간 네모가 kernel image / link 파일은 현재 사용 중인 kernel image의 심볼릭 링크를 의미

 

리눅스 커널 이미지는 일반적으로 압축되어 관리되는데, 부트로더가 압축을 풀어 메모리에 적재한다.

 - zImage : gzip으로 압축된 kernel image

 - bzImage : 파일 크기가 큰 kernel image (통상 1MB 이상 수준)

 

그래서 나만의 리눅스 운영체제를 제작할 수 있다.

1) 원하는 버전의 리눅스 커널을 인터넷에서 다운로드
2) 리눅스 커널 수정
    - 디바이스 드라이버 개발, 추가, 나만의 OS에 맞춰 각종 소스코드 수정
3) 리눅스 커널을 컴파일(build)해서 리눅스 커널 이미지를 생성
4) 부트로더를 인터넷에서 다운로드
    - 보통은 리눅스의 경우, GRUB이 같이 빌드 된다. 그 외의 부트로더를 쓰고 싶다면 다운로드받으면 된다.
5) Board 동작 시키기
    - 보드에 부트로더와 리눅스 커널 이미지를 함께 사용해서 보드를 동작 <-- Bring Up 이라고 함

 


PC Booting Process

 

CMOS (Complementary Metal-Oxide-Semiconductor)

 - 비휘발성 메모리가 들어있는 하드웨어

 - 메모리 크기, 부팅 순서, HW 구성 정보, 설정 값 등의 CMOS Data를 저장함
 -
배터리 전원을 사용

 - 부팅 시, BIOS가 CMOS에 있는 정보를 가져와 부팅을 시작

BIOS (Basic Input/Output System)

기본적인 I/O 를 위한 Firmware
 - 컴퓨터 부팅시 바로 BIOS(Firmware) 가 동작을 시작
 - ROM BIOS 에 BIOS 설정 Utility가 들어있음
 - CMOS의 설정값을 변경 가능

 

 

POST (Power-on self-test)

BIOS에서 Power를 켜자마자 주변장치들을 검사하는 과정

 - BIOS가 POST를 하고 있을 때 , Log Message가 출력 됨

옛날 컴퓨터 POST 진행 화면

 

UEFI (Unified Extensible Firmware Interface)

64bit 기반 LBA 시스템

- BIOS와의 차이점

     - 화려한 그래픽 UI

     - 2.2TB 이상 디스크 사용을 위한 GPT(GUID Partition Table) 지원

UEFI 화면

 

GRUB (GRand Unified Bootloader 2)
GNU 프로젝트에서 개발한 부트로더
- 현 대부분 리눅스 배포판은 GRUB2를 사용

 


GRUB 설정 파일 변경

$ sudo vi /etc/default/grub

 

위의 네모 박스와 같이 값을 수정해 줌

$ sudo update-grub

해당 명령어를 실행해야, grub 수정 사항이 저장된다.

 

수정 사항은 "/boot/grub/grub.cfg"에 update-grub 명령어를 통해 환경설정 파일이 만들어진다.

이후 재부팅 진행

$ sudo reboot

다음 키를 누르면, 키에 설정된 옵션이 진행된다.

 - E : grub.cnf 내용 확인
 - C : command 모드로 진입

 

여러 옵션이 있으니 나중에 찾아보면서 설정을 바꿔보자

 


U-boot (Universal Bootloader)

컴퓨터의 부팅 프로세스를 제어하는 오픈 소스 부트로더

 

*ARM : "Advanced RISC Machines"의 약어로, 저전력 임베디드 시스템에서 많이 사용되는 프로세서 아키텍처

*ROM : "Read-Only Memory"의 약어로, 컴퓨터 시스템에서 사용되는 기억장치

PC 계열과 ARM 부트로더의 차이

x64_86
 - 0 단계 : ROM 코드
 - 1 단계 : BIOS or UEFI
 - 2 단계 : Bootloader (GRUB)
 - Linux Kernel 실행

ARM
 - 0, 1 단계 : 칩셋 사 제공
 - 2 단계 : Bootloader (u-boot)
 - Linux Kernel 실행

ARM에서 BL0, BL1은 칩셋 사에서 담당, BL2부터는 U-boot가 동작

 


Rpi4에서 U-boot 동작시키기

1. Rpi4 - UART - Desktop 시리얼 통신 형성

 

Rpi4 - UART 연결

PC - UART - Rpi4 연결을 통해 시리얼 통신이 가능하도록 만든다.

 

Rpi4에서, Serial 환경 설정 Enabled로 바꾸기

 

Desktop에서, UART Driver 설치

https://ftdichip.com/drivers/vcp-drivers/

모델명 : FT232BL

현재 support되는 driver 버전을 설치할 것

압축 해제 후, 해당 설치 파일을 설치 후 재부팅하면 된다.

 

UART 테스트 진행

mobaXterm에서 지원하는 Serial 연결을 이용, 아래 사진처럼 setting 진행

 


2. U-boot 이미지 빌드

 

U-boot 이미지 생성을 위한 환경 설정

RaspberryPi, ubuntu 두 곳에 모두 진행할 것

# Requirement 설치
$ sudo apt install git vim gcc
$ sudo apt install bc bison flex libssl-dev make libc6-dev
$ sudo apt install libncurses5-dev # 
$ sudo apt install crossbuild-essential-armhf # 크로스컴파일 툴체인 설치

# Home directory에 설치
$ git clone https://github.com/u-boot/u-boot.git

# config 파일 수정
$ cd ./u-boot
$ sudo vi config.mk

config.mk 파일 내에 다음 문장 추가 (오타 주의) --> CROSS_COMPILE make 변수 추가

CROSS_COMPILE := arm-linux-gnueabihf-

 

.config 파일 생성

마찬가지로 RaspberryPi, ubuntu 두 곳에 모두 진행할 것

# U-boot 폴더 내에서 진행
$ sudo make rpi_4_32b_defconfig

 

.config 파일 변경

.config 파일 변경 시에는 command line interface를 이용한다. (수동으로 작업 x)

$ make menuconfig

 

u-boot 빌드하기

RaspberryPi에서만 진행할 것

$ sudo make -j4 # 코어 개수에 맞게 숫자 설정

# 완료 후, 빌드된 u-boot.bin 파일을 /boot 디렉토리에 이동시키기
$ sudo cp ./u-boot.bin /boot/.

 

u-boot로 부팅하기 위한 사전 작업 진행 

마찬가지로, RaspberryPi에서만 진행할 것

 

# /boot 디렉토리에, config.txt 수정

$ cd /boot/

# 해당 파일이 복사되었는 지 확인
$ ls -al u-boot.bin

# config 수정
$ sudo vi ./config.txt

위 그림처럼, config.txt 맨 아래에 kernel=u-boot.bin 을 가장 아래에 추가

 

해당 세팅을 저장하면, Rpi4가 기존 부트로더로 부팅하지 않고 u-boot.bin을 활용해 부팅을 한다. 이유는 아래 그림을 참고

라즈베리파이 부팅 진행 순서

 


3. 재부팅

$ sudo shutdown -h now
부팅 진입 순서

1. 라즈베리파이의 전원 빼기
2. UART USB 빼기 <-- USB를 빼야 Rpi의 전원이 완전히 끊김
3. 1 초간 대기
4. UART USB 연결
5. 라즈베리파이 전원 연결
6. Serial 로 진입

본인의 경우에는 Timeout 메세지가 연속으로 뜨다가 연결됨

U-boot command는 document를 찾아볼 것

 


U-boot Custom Command 생성

해당 방법을 을용해서 Device Control도 가능하다.

 

먼저 ubuntu에서 다음 파일을 보자.

$ cd ~/u-boot/cmd
$ ls -al echo.c

echo.c 파일의 코드 구성

위 코드를 응용해서 custom command를 만들어보자

 

echo.c 파일 복사해서 template 만들기

# echo.c 복사
$ sudo cp ./echo.c ./custom.c

$ sudo vi custom.c

1. :%s/echo/custom/g : 코드 내 echo를 모두 custom으로 바꿈

2. max argument를 0으로 수정

 

cmd/Kconfig 파일 수정 후 저장

echo와 양식을 맞춰서 이름만 변경 후 기입

 

cmd/Makefile 파일 수정 후 저장

echo로 검색해서 복사 붙여넣기를 통해 CUSTOM 이름으로 한 줄 더 생성

 

Custom 명령어 등록 체크

$ cd ~/u-boot
$ sudo make menuconfig

Command line interface 진입 후 / 입력 시, 검색창에서 "custom" 검색하면 명령어가 뜬다.

Save 버튼을 누른 후 나오기

 

u-boot.bin 파일 생성

$ cd ~/u-boot

$ sudo make -j4

 

RaspberryPi 부팅 파일 삽입

이제 Ubuntu에서 생성한 u-boot.bin 파일을, Raspberry Pi의 microSD 카드의 /boot/ 디렉토리에 삽입

이후 UART를 이용해서 command를 입력 시, command가 작동하는 것을 알 수 있다.