pi@pi14:~$ uname -a
Linux pi14 6.1.0-rpi7-rpi-v7l #1 SMP Raspbian 1:6.1.63-1+rpt1 (2023-11-24) armv7l GNU/Linux
커널버전: 6.1.0-rpi7-rpi-v7l (라즈베리파이4의 32비트용 커널 = rpi7 / 64비트용 커널 = rpi8)
우리가 하고자 하는 것은 커널 소스를 받아와서 커널을 업그레이드 하는 것이다.
어플리케이션에서 커널로 진입하기 위한 관문이 시스템 콜 함수이다. 그렇기 때문이 이미 만들어져있는 수백개의 시스템 콜 함수를 이해하고 사용하는게 정말 중요하다.
우리가 최종적으로 하고자 하는 것은 디바이스 드라이버를 만드는 것이다. 디바이스 드라이버를 만들기 위해서는 시스템 콜이 반드시 필요하다. 그렇기 때문에 시스템 콜 함수를 잘 알아야 하는 것이다.
종국적으로 우리는 우리만의 커널을 조금 커스텀 해서 커스텀 시스템 콜 함수를 만들고, 그걸로 디바이스 드라이버를 제어해볼 것이다. (이게 현업에서 요구되는 역량인 것이다.)
ubuntu@ubuntu14:~/pi_bsp/kernel$ sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev
ubuntu@ubuntu14:~/pi_bsp/kernel$ git clone --depth=1 -b rpi-6.1.y https://github.com/raspberrypi/linux
ubuntu@ubuntu14:~/pi_bsp/kernel$ du linux | wc -l
5135
ubuntu@ubuntu14:~/pi_bsp/kernel$ du -h linux
...
1.7G linux/
ubuntu@ubuntu14:~/pi_bsp/kernel$ du -h -d 1 linux/
204K linux/init
3.4M linux/security
52M linux/include
276K linux/ipc
36M linux/net
63M linux/tools
2.0M linux/block
80K linux/usr
3.9M linux/scripts
46M linux/fs
460K linux/rust
5.2M linux/mm
13M linux/kernel
72K linux/certs
296K linux/virt
3.8M linux/crypto
48M linux/sound
40K linux/.github
7.7M linux/lib
243M linux/.git
276K linux/LICENSES
1.8M linux/samples
580K linux/io_uring
146M linux/arch
949M linux/drivers
64M linux/Documentation
1.7G linux/
리눅스 압축/해체 블로그 참고
// 디렉토리 갯수 확인
ubuntu@ubuntu14:~/pi_bsp/kernel$ du linux | wc -l
5135
// 파일 갯수 확인
ubuntu@ubuntu14:~/pi_bsp/kernel$ find | wc -l
84603
// 파일을 하나로 묶어버리기 (압축한거 아님)
ubuntu@ubuntu14:~/pi_bsp/kernel$ tar cvf linux.tar linux
ubuntu@ubuntu14:~/pi_bsp/kernel$ ls -l
total 1583840
drwxrwxr-x 28 ubuntu ubuntu 4096 2월 15 09:50 linux
-rw-rw-r-- 1 ubuntu ubuntu 1621841920 2월 15 09:58 linux.tar
// 묶인 파일 압축하기
ubuntu@ubuntu14:~/pi_bsp/kernel$ gzip linux.tar
ubuntu@ubuntu14:~/pi_bsp/kernel$ ls -l
total 462456
drwxrwxr-x 28 ubuntu ubuntu 4096 2월 15 09:50 linux
-rw-rw-r-- 1 ubuntu ubuntu 473543467 2월 15 09:58 linux.tar.gz
// 압축 풀어보기
ubuntu@ubuntu14:~/pi_bsp/kernel$ gzip -d linux.tar.gz
ubuntu@ubuntu14:~/pi_bsp/kernel$ tar xvf linux.tar
"tar zcvf" = "tar cvf" + "gzip"
"tar zxvf" = "tar xvf" + "gzip -d"
"tar jcvf" = "tar cvf" + "bzip2"
"tar jxvf" = "tar xvf" + "bzip2 -d"
gzip은 압축률이 좀 떨어진다.
gzip보다 압축률이 좀더 좋은 걸로 bzip2가 있다.
압축률이 높으면 압축하는데 좀 오래 걸리는데, 그래도 압축률이 높은게 좋다.
예를들어 네트워크로 파일을 전송하는 경우를 생각해보자. 파일 사이즈가 적을수록 패킷이 적게 들기 때문에 네트워크 전송하는데 드는 비용이 감소한다. 그래서 보통은 압축률이 높은게 좋다.
참고) kernel.org라는 공식사이트에서 리눅스 공식 커널을 배포하고 있다.
부트로더나 커널이나 폴더 구조는 비슷하다. (실제로 u-boot랑 raspberry kernel이랑 폴더구조가 비슷하다.)
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ cd arch/arm/configs/
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/arch/arm/configs$ ls
am200epdkit_defconfig ezx_defconfig multi_v4t_defconfig s5pv210_defconfig
aspeed_g4_defconfig footbridge_defconfig multi_v5_defconfig sama5_defconfig
aspeed_g5_defconfig gemini_defconfig multi_v7_defconfig sama7_defconfig
assabet_defconfig h3600_defconfig mv78xx0_defconfig shannon_defconfig
at91_dt_defconfig h5000_defconfig mvebu_v5_defconfig shmobile_defconfig
axm55xx_defconfig hackkit_defconfig mvebu_v7_defconfig simpad_defconfig
badge4_defconfig hisi_defconfig mxs_defconfig socfpga_defconfig
bcm2709_defconfig imx_v4_v5_defconfig neponset_defconfig sp7021_defconfig
bcm2711_defconfig imx_v6_v7_defconfig netwinder_defconfig spear13xx_defconfig
bcm2835_defconfig imxrt_defconfig nhk8815_defconfig spear3xx_defconfig
bcmrpi_defconfig integrator_defconfig omap1_defconfig spear6xx_defconfig
cerfcube_defconfig iop32x_defconfig omap2plus_defconfig spitz_defconfig
clps711x_defconfig ixp4xx_defconfig orion5x_defconfig stm32_defconfig
cm_x300_defconfig jornada720_defconfig oxnas_v6_defconfig sunxi_defconfig
cns3420vb_defconfig keystone_defconfig palmz72_defconfig tct_hammer_defconfig
colibri_pxa270_defconfig lart_defconfig pcm027_defconfig tegra_defconfig
colibri_pxa300_defconfig lpc18xx_defconfig pleb_defconfig trizeps4_defconfig
collie_defconfig lpc32xx_defconfig pxa168_defconfig u8500_defconfig
corgi_defconfig lpd270_defconfig pxa255-idp_defconfig versatile_defconfig
davinci_all_defconfig lubbock_defconfig pxa3xx_defconfig vexpress_defconfig
dove_defconfig magician_defconfig pxa910_defconfig vf610m4_defconfig
dram_0x00000000.config mainstone_defconfig pxa_defconfig viper_defconfig
dram_0xc0000000.config milbeaut_m10v_defconfig qcom_defconfig vt8500_v6_v7_defconfig
dram_0xd0000000.config mini2440_defconfig realview_defconfig xcep_defconfig
ep93xx_defconfig mmp2_defconfig rpc_defconfig zeus_defconfig
eseries_pxa_defconfig moxart_defconfig s3c2410_defconfig
exynos_defconfig mps2_defconfig s3c6400_defconfig
라즈베리파이 구동에 필요한 환경설정 파일이 여기에 다 이미 만들어져 있다.
이중에서 우리가 건드려야 할 파일은 bcm2711_defconfig이다.
우선 다시 최상위 디렉토리로 돌아오고 아래 명령어를 입력하자.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/confdata.o
HOSTCC scripts/kconfig/expr.o
LEX scripts/kconfig/lexer.lex.c
YACC scripts/kconfig/parser.tab.[ch]
HOSTCC scripts/kconfig/lexer.lex.o
HOSTCC scripts/kconfig/menu.o
HOSTCC scripts/kconfig/parser.tab.o
HOSTCC scripts/kconfig/preprocess.o
HOSTCC scripts/kconfig/symbol.o
HOSTCC scripts/kconfig/util.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
최초 한번 빌드할 때는 반드시 한번 컨피그파일을 가져와야 한다고 했다.
그래서 위 명령어를 통해 .config라는 파일이 복사되어 최상위 폴더에 만들어졌다.
단, .config라는 파일은 vi 에디터로 직접 변경하면 안된다.
반드시 아래 명령어를 통해 열리는 텍스트기반 GUI에서 변경해야 한다.
(u-boot에서 했던 것과 동일하다.)
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
반드시 앞선 명령으로 ".config" 파일이 만들어진 것을 확인하고 빌드를 시작해야 한다!!
원본명령은 다음과 같다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs -j4
커널 자체를 빌드하는데는 얼마 안걸리는데, 진짜 엄청나게 많은 디바이스드라이버를 빌드하는데 시간이 엄청나게 많이 걸린다. (우리 컴퓨터는 엄청나게 좋은데도 불구하고 거의 30분에서 1시간이 걸릴 것이다.)
zImage: 리눅스 커널 이미지를 빌드합니다. zImage는 압축된 커널 이미지를 나타냅니다. 이는 초기 부트로더가 해제하고 실행할 수 있는 형태의 커널 이미지입니다.
modules: 리눅스 커널 모듈을 빌드합니다. 모듈은 커널이 실행 중에 동적으로 로드되거나 언로드되는 확장 모듈로, 필요에 따라 추가 기능이나 드라이버를 추가할 수 있도록 해줍니다.
dtbs: Device Tree Blob (DTB) 파일을 빌드합니다. DTB는 리눅스 커널이 부팅할 때 하드웨어 구성을 설명하는 바이너리 형태의 데이터로, 특히 임베디드 시스템에서 사용됩니다.
-j4: 동시에 수행되는 작업의 개수를 지정합니다. -j4는 최대 4개의 작업을 동시에 수행하도록 지정하는 것으로, 빌드 프로세스를 가속화하고 빌드 시간을 단축하는 데 도움이 됩니다.
여기서 -j4는 빌드 프로세스를 병렬로 수행하여 시간을 절약하는데 사용됩니다. 만약 사용 가능한 CPU 코어의 수가 더 많다면, -j 옵션 뒤에 사용하고자 하는 CPU 코어의 수를 지정하여 더 많은 병렬 작업을 수행할 수 있습니다.
우리 컴퓨터 CPU가 훨씬 좋았는데 코어를 4개만 줄수 있었던 이유는 이 우분투 자체가 가상환경에서 코어 4개를 할당받아서 실행되고 있기 때문이다.
최초 한번은 디바이스 파일과 디바이스 트리 파일들을 전부다 라즈베리쪽에 넘겨줘야 한다..
그 이후부터는 커널 이미지만 nfs로 넘겨주면 되기 때문에 문제가 안된다.
라즈베리파이에서 SD카드 뺐다.
SD카드를 리더기에 넣고 윈도우에 꼈다.
우분투 가상환경에 인식시켜줬다. (메뉴바 장치 - USB - Generic Mass storage decive)
그리고 아래 명령어를 순서대로 우분투 터미널에서 입력해주면 된다.
// sd카드 제대로 인식되었는지 확인
$ df
...
/dev/sdb1 261108 75820 185288 30% /media/ubuntu/bootfs
/dev/sdb2 30322460 15917604 13048344 55% /media/ubuntu/rootfs
// 모듈 설치
~/pi_bsp/kernel/linux$ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/media/ubuntu/rootfs modules_install
// 모듈 추가되었는지 확인
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ ls -l /media/ubuntu/rootfs/lib/modules/
total 20
drwxr-xr-x 3 root root 4096 12월 5 10:07 6.1.0-rpi7-rpi-v6
drwxr-xr-x 3 root root 4096 12월 5 10:06 6.1.0-rpi7-rpi-v7
drwxr-xr-x 3 root root 4096 12월 5 10:06 6.1.0-rpi7-rpi-v7l
drwxr-xr-x 3 root root 4096 12월 5 10:06 6.1.0-rpi7-rpi-v8
drwxr-xr-x 3 root root 4096 2월 15 11:30 6.1.77-v7l+
// 기존에 있던 이미지를 백업용으로 만들어놓기
~/pi_bsp/kernel/linux$ sudo mv /media/ubuntu/bootfs/kernel7l.img /media/ubuntu/bootfs/kernel7l-backup.img
// 앞서 빌드했던 이미지(zImage)를 kernel7l.img라는 이름으로 복붙
~/pi_bsp/kernel/linux$ sudo cp arch/arm/boot/zImage /media/ubuntu/bootfs/kernel7l.img
// 앞서 빌드했던 dtb 파일을 복붙
~/pi_bsp/kernel/linux$ sudo cp arch/arm/boot/dts/*.dtb /media/ubuntu/bootfs/
// 앞서 빌드했던 overlays들을 복붙
~/pi_bsp/kernel/linux$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /media/ubuntu/bootfs/overlays/
// 앞서 빌드하면서 생성된 Readme를 복붙
~/pi_bsp/kernel/linux$ sudo cp arch/arm/boot/dts/overlays/README /media/ubuntu/bootfs/overlays/
위 명령어를 통해 sd카드에 이미지를 집어넣고 아래 명령어를 통해 config 파일을 수정해준다.
~/pi_bsp/kernel/linux$ sudo vi /media/ubuntu/bootfs/config.txt
위 명령어로 들어온 파일의 맨 아래에다가 아래 라인을 추가한다.
arm_64bit=0
이제 다시 라즈베리파이를 구동시키고 /boot/firmware/config.txt 파일을 sudo권한으로 열어서 아래 라인을 주석처리 해준다. 이제 u-boot 부트로더를 거치지 않고 바로 커널로 부팅되도록 하겠다.
#kernel=u-boot.bin
pi@pi14:~$ cd /lib/modules/6.1.77-v7l+/kernel
pi@pi14:/lib/modules/6.1.77-v7l+/kernel$ ls
pi@pi14:/lib/modules/6.1.77-v7l+/kernel$ ls
pi@pi14:/lib/modules/6.1.77-v7l+/kernel$ find . | wc -l
라즈베리파이 켤때 나오는 딸기 갯수는 해당 머신의 코어 갯수이다.
쿼드코어라서 딸기 4개가 나왔다.
지금부터 그 딸기 이미지를 핑크빈 이미지로 바꿔보자.
(이 실습의 목적은 커널 소스를 수정해보는 것이다.)
리눅스에서 간단하게 이미지를 보고 싶을 때는 eog 명령을 사용하면 된다.
$ eog
오픈소스 이미지 편집기(gimp)을 통해서 핑크빈 이미지를 바꿔보겠다.
gimp = GNU image 편집 program
설치 및 실행은 우분투 데스크탑 GUI 환경에서 진행하겠다.
$ sudo apt install gimp
$ gimp
커널이 뜰때 뜨는 이미지는 프레임 버퍼를 통해서 이미지를 가져오기 때문에 반드시 그 이미지 픽셀 수가 정확하게 맞아야지 이미지가 똑바로 뜬다.
(알아서 막 맞춰주고 그렇지 않다는 것이다.)
그래서 우리는 딱 그 버퍼 크기에 맞게 그림 크기를 바꿔줘야 한다.
목표는 가로 800 x 세로 480 사이즈로 만드는 것이다. (딱 이 크기가 이미지 버퍼 사이즈이다.)
사슬을 끊어줘야 비율을 무시하겠다는 뜻이 되어 800 480 입력이 된다.
export as 를 누르고 파일 포맷을 바꿔주겠다.
파일 유형에서 PPM 이미지를 클릭한다.
그리고 데이터 타입은 ASCII 타입으로 해서 내보내기 한다.
가로 800 x 세로 480 사이즈의 ppm이미지가 준비되었다.
ppm은 리눅스의 일반 이미지 파일 포맷이다.
그래서 이걸 숫자형태로 바꿔줘야 한다.
Netpbm은 다양한 이미지 포맷을 변환하고 편집하기 위한 무료 오픈 소스 이미지 처리 툴킷입니다. Netpbm은 PBM (Portable BitMap), PGM (Portable GrayMap), PPM (Portable PixMap)과 같은 포터블 비트맵 이미지 포맷을 처리하는 데 주로 사용됩니다.
// 변환 툴 설치
ubuntu@ubuntu14:~/pi_bsp/kernel$ sudo apt install netpbm
// 이미지 변환(이미지 포맷 -> 바이너리 포맷)
ubuntu@ubuntu14:~/pi_bsp/kernel$ pnmquant -fs 224 cute_pink_bean.ppm > logo_kcci_224.ppm
pnmcolormap: making histogram...
pnmcolormap: too many colors!
pnmcolormap: scaling colors from maxval=255 to maxval=127 to improve clustering...
pnmcolormap: making histogram...
pnmcolormap: 28845 colors found
pnmcolormap: choosing 224 colors...
pnmremap: 223 colors found in colormap
// 위 과정으로 생긴 파일을 다시 한번 변환(바이너리 포맷 -> 숫자포맷)
ubuntu@ubuntu14:~/pi_bsp/kernel$ pnmnoraw logo_kcci_224.ppm > logo_kcci_clut224.ppm
// 숫자 값으로 바뀌었는지 확인
ubuntu@ubuntu14:~/pi_bsp/kernel$ cat logo_kcci_clut224.ppm
// 커널 디렉토리로 복붙하기
ubuntu@ubuntu14:~/pi_bsp/kernel$ cp logo_kcci_clut224.ppm linux/drivers/video/logo/
// 로고 관련 config 파일 수정
ubuntu@ubuntu14:~/pi_bsp/kernel$ cd linux/drivers/video/logo/
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/video/logo$ ls
Kconfig logo.o logo_linux_clut224.ppm logo_sgi_clut224.ppm logo_superh_vga16.ppm
Makefile logo_dec_clut224.ppm logo_linux_mono.pbm logo_spe_clut224.ppm modules.order
built-in.a logo_kcci_clut224.ppm logo_linux_vga16.ppm logo_sun_clut224.ppm pnmtologo
clut_vga16.ppm logo_linux_clut224.c logo_mac_clut224.ppm logo_superh_clut224.ppm pnmtologo.c
logo.c logo_linux_clut224.o logo_parisc_clut224.ppm logo_superh_mono.pbm
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/video/logo$ vi Kconfig
참고) 항상 소스가 있는 파일에는 Makefile, Kconfig(혹은 config) 파일들이 셋트로 들어있다.(커널 소스를 수정하고 건드릴 때는 Source - Kconfig - Makefile을 셋트로 수정해줘야 한다는 것이다!!!)
참고) Kconfig 파일은 리눅스 커널의 설정 파일로, 커널의 여러 기능과 옵션을 구성하는 데 사용됩니다. 이 파일은 사용자가 빌드하려는 커널의 구성을 지정하고, 어떤 기능을 활성화하거나 비활성화할지를 선택할 수 있는 환경을 제공합니다.
위 명령으로 Kconfig 파일을 열어서 아래 명령어 4줄을 맨 아래에 추가해준다.
// 해당 디렉토리의 Makefile도 수정하기
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/video/logo$ vi Makefile
위 명령으로 Makefile 파일을 열어서 아래 명령어 한줄을 16번째 라인에 추가해준다.
(obj-$(CONFIG_LOGO_KCCI_CLUT224) += logo_kcci_clut224.o)
그리고 이제 logo.c 파일을 수정해준다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/video/logo$ vi logo.c
위 명령으로 logo.c 파일을 열어서 아래 코드라인을 추가한다.
(#ifdef ~ #endif 세줄 추가해준거다.)
아래 그림에서 logo 변수에 담기는 "&logo_kcci_clut224"는 구조체이다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/drivers/video/logo$ vi logo_kcci_clut224.c
위 명령을 통해서 구조체 내부를 확인해 볼수 있다. 그리고 이 파일은 컴파일 하면서 생성된 것이다.
그리고 이제 logo.h 파일도 수정해준다.(경로 달라졌다!!!)
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/include/linux$ vi linux_logo.h
위 명령으로 logo.h 파일을 열어서 아래 코드라인을 추가한다.
(48번째 라인 수정해준거다.)
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
메뉴 콘피그를 열어준다. 우리는 지금 .config파일을 수정하고 싶은 것이다.
menuconfig에서 옵션 접근 순서는
- Device Drivers
- Graphics support
- Bootup logo
- "kcci 224-color Linux logo"만 체크
이제 우리는 커널만 컴파일 하면 된다. 드라이버를 모듈들을 다시 컴파일 할 필요가 없다.
그러므로 오직 "zImage"만 컴파일 하면 된다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j4
SYNC include/config/auto.conf.cmd
CALL scripts/checksyscalls.sh
CC drivers/video/logo/logo.o
LOGO drivers/video/logo/logo_kcci_clut224.c
CC drivers/video/logo/logo_kcci_clut224.o
AR drivers/video/logo/built-in.a
CC drivers/video/fbdev/core/fbmem.o
AR drivers/video/fbdev/core/built-in.a
AR drivers/video/fbdev/built-in.a
AR drivers/video/built-in.a
AR drivers/built-in.a
AR built-in.a
AR vmlinux.a
LD vmlinux.o
OBJCOPY modules.builtin.modinfo
GEN modules.builtin
GEN .vmlinux.objs
MODPOST vmlinux.symvers
UPD include/generated/utsversion.h
CC init/version-timestamp.o
LD .tmp_vmlinux.kallsyms1
NM .tmp_vmlinux.kallsyms1.syms
KSYMS .tmp_vmlinux.kallsyms1.S
AS .tmp_vmlinux.kallsyms1.S
LD .tmp_vmlinux.kallsyms2
NM .tmp_vmlinux.kallsyms2.syms
KSYMS .tmp_vmlinux.kallsyms2.S
AS .tmp_vmlinux.kallsyms2.S
LD vmlinux
NM System.map
SORTTAB vmlinux
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy_data
AS arch/arm/boot/compressed/piggy.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
커널 이미지가 빌드되는 과정을 보면 우리가 수정했던 "LOGO drivers/video/logo/logo_kcci_clut224.c"가 컴파일 되는 것을 볼 수 있다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux$ cp arch/arm/boot/zImage /srv/nfs/
이제 nfs로 빌드된 커널 이미지를 라즈베리파이로 옮겨서 덮어쓰기 해주고 부팅해보겠다.(여기부터는 라즈베리파이다. 터미널 어디서 치는건지 잘 봐라.)
pi@pi14:/mnt/ubuntu_nfs$ sudo cp zImage /boot/firmware/kernel7l.img
pi@pi14:/mnt/ubuntu_nfs$ sync