5 분 소요

지난 글에서 PH12/EM_INT/RCD_INT가 재부팅 직후 잠깐 켜졌다 꺼지는 걸 확인했다. 원인은 이미 알고 있었다 — U-Boot이 LTDC를 초기화하고, 커널이 넘겨받는 그 사이 구간. 커널 DTS로는 못 막는 영역이라 U-Boot 소스를 직접 고쳐야 했다.

결과부터 말하면 끝냈다. 다만 가는 길에 생각보다 많은 게 걸렸다.


소스부터 확인 — 진짜 그 바이너리가 맞는지

MYiR 사이트에서 U-Boot 소스를 받았다. 받았다고 바로 믿을 게 아니라, 지금 보드에 올라가 있는 그 바이너리랑 같은 소스인지부터 확인했다.

root@myir:~# printenv
...
ver=U-Boot 2020.01-stm32mp-r1 (Nov 03 2021 - 07:47:19 +0000)

받은 소스의 git 로그를 봤다.

$ git log -1 --format="%H %ad"
62f4cbc4 Wed Nov 3 00:47:19 2021 -0700

-0700을 UTC로 바꾸면 07:47:19 — 초 단위까지 정확히 일치했다. 이 소스가 맞다.


&ltdc만 끄면 안 됐다

지난 글에서 U-Boot의 stm32mp15-u-boot.dtsi에 이런 게 있다고 짐작했었다.

/* pre-reloc probe = reserve video frame buffer in video_reserve() */
&ltdc {
	u-boot,dm-pre-proper;
};

실제로 있었다. env·splash 설정과 무관하게 무조건 이른 시점에 probe되도록 박혀 있었다. 그래서 지금까지 환경변수로는 못 잡았던 거다.

그런데 보드 파일(stm32mp157c-ya157c-512d-v2.dts)을 열어보니, &ltdc만 끄는 걸로는 부족했다.

panel_rgb: panel-rgb {
	compatible = "simple-panel";
	pinctrl-0 = <&ltdc_pins_a>;   /* PH12/13/14/15 등을 AF14로 */
	backlight = <&panel_backlight>;
	status = "okay";
	...
};

&ltdc {
	status = "okay";
	//pinctrl-names = "default";   /* ← 주석 처리돼 있다 */
	...
};

&ltdc 자신은 pinctrl-names가 주석 처리돼 있어서 핀을 안 잡는다. 실제로 PH12/13/14/15를 드라이브하는 건 panel_rgb다. 별도 디바이스라서 자기 pinctrl-0을 독립적으로 적용한다. &ltdc만 껐으면 panel_rgb가 여전히 그 핀들을 잡았을 거다.

하나 더 있었다.

panel_backlight: panel-backlight {
	compatible = "gpio-backlight";
	gpios = <&gpiob 8 GPIO_ACTIVE_HIGH>;   /* PB8 = 우리 보드 C_AC_ON */
	default-on;
	status = "okay";
};

default-on. probe되는 순간 PB8을 HIGH로 민다. 커널 쪽에서 이미 /delete-node/로 지웠던 바로 그 문제가, U-Boot 쪽에도 그대로 있었다.

셋 다 껐다.

&panel_rgb       { status = "disabled"; };
&panel_backlight { status = "disabled"; };
&ltdc            { status = "disabled"; };

&ltdc만 보고 끝냈으면 절반만 고친 채로 “됐다”고 착각했을 거다.


빌드 — 에러가 네 개 연달아 나왔다

빌드 스크립트는 커널 것(kbuild.sh)을 거의 그대로 재사용했다. 그런데도 U-Boot 특유의 문제가 네 개 연달아 걸렸다. 하나씩 원인을 짚었다.

1. multiple definition of yylloc

/usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss+0x10): multiple definition of `yylloc'

이 U-Boot(2020년산)에 번들된 dtc 파서 소스가 오래됐다. GCC 10부터 기본값이 -fno-common으로 바뀌면서, 예전엔 봐주던 전역변수 중복 선언이 진짜 에러가 됐다.

make ... HOSTCFLAGS=-fcommon

2. undefined reference to image_get_checksum_algo

/usr/bin/ld: tools/image-host.o: in function `fit_image_setup_sig':
undefined reference to `image_get_checksum_algo'

처음엔 pkg-config가 아예 없어서 그런가 싶었다.

$ pkg-config --exists libcrypto
bash: pkg-config: command not found

설치했다.

sudo apt-get install -y pkg-config

3. 같은 에러가 또 났다

pkg-config를 설치했는데도 똑같은 에러였다. .config를 열어보고서야 진짜 원인을 찾았다.

# CONFIG_FIT_SIGNATURE is not set

tools/Makefile을 보면 이유가 나온다.

FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o

image_get_checksum_algo의 실제 정의가 있는 common/image-sig.o는 CONFIG_FIT_SIGNATURE가 켜져 있을 때만 컴파일된다. 그런데 tools/image-host.c의 fit_image_setup_sig()는 이 함수들을 조건 없이 호출한다. 정의는 빠지고 호출부만 남는 비대칭이 생긴 거다. pkg-config는 애초에 무관한 문제였다.

sed -i 's/# CONFIG_FIT_SIGNATURE is not set/CONFIG_FIT_SIGNATURE=y/' .config
make olddefconfig

4. __WORDSIZE is not defined — 수백 줄

가장 알아보기 어려웠던 에러다.

error: #error "__WORDSIZE is not defined"
error: unknown type name '__STD_TYPE'

stdint.h, sys/types.h 같은 기본 헤더가 통째로 깨졌다. 호스트용 gcc가 갑자기 ARM 타깃 헤더를 읽고 있다는 신호였다.

pkg-config를 직접 쳐보고서야 잡았다.

$ pkg-config --cflags libcrypto
-I/opt/st/myir/3.1-snapshot/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/include

ARM sysroot 경로가 그대로 나왔다. SDK가 export한 PKG_CONFIG_SYSROOT_DIR이 범인이었다. 이 변수는 원래 “다른 패키지를 ARM용으로 크로스 컴파일할 때” 쓰라고 있는 건데, U-Boot의 호스트 툴(mkimage)이 OpenSSL을 찾는 과정에서도 그대로 새어 들어간 거다. 호스트 gcc가 ARM용 stdint.h를 읽으니 __WORDSIZE가 정의될 리 없었다.

unset PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_PATH

네 개를 정리하면 이렇다.

# 증상 진짜 원인 조치
1 yylloc 중복 정의 GCC 10+ 기본값 변경 vs 구형 dtc HOSTCFLAGS=-fcommon
2 undefined reference pkg-config 없음 (곁가지) 설치
3 같은 에러, 설치 후에도 지속 CONFIG_FIT_SIGNATURE 꺼짐 (진짜 원인) .config 수정
4 __WORDSIZE 대량 에러 PKG_CONFIG_SYSROOT_DIR가 ARM sysroot로 새어들어감 unset

2번에서 멈췄으면 3번을 못 찾았을 거고, 매번 에러 메시지가 가리키는 곳과 진짜 원인이 다른 곳에 있었다. kbuild.sh에 다음처럼 다 몰아넣고서야 한 번에 통과했다.

#!/bin/bash
set -e
. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
unset LDFLAGS CFLAGS CPPFLAGS CXXFLAGS KCFLAGS PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_PATH
export PATH=/usr/local/bin:/usr/bin:/bin:$PATH
cd ~/work/U-boot
exec make O="$HOME/work/build-trusted" ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- "$@"

빌드 산출물과, 디컴파일해서 실제로 값이 들어갔는지 확인했다.

$ dtc -I dtb -O dts u-boot.dtb | grep -A5 panel-rgb
panel-rgb {
	...
	status = "disabled";

panel-backlight, ltdc 노드도 전부 disabled로 확인됐다.


배포 — 라벨을 믿었다가 하마터면 eMMC를 쓸 뻔했다

이번 작업에서 가장 아찔했던 순간이다.

SSBL 파티션을 찾으려고 이렇게 쳤다.

root@myir:~# ls -l /dev/disk/by-partlabel/
fsbl1 -> ../../mmcblk1p1
fsbl2 -> ../../mmcblk1p2
ssbl  -> ../../mmcblk2p1     ← 이거
...

ssbl이 mmcblk2를 가리켰다. 이 프로젝트에서 mmcblk2는 eMMC다. 처음부터 “eMMC는 건드리지 않는다”로 지켜온 원칙에 정면으로 걸릴 뻔했다.

/dev/disk/by-partlabel/는 라벨 하나당 링크 하나만 만든다. SD카드(mmcblk1)와 eMMC(mmcblk2) 양쪽 다 ssbl이라는 이름의 파티션을 갖고 있으면, udev가 나중에 잡은 쪽만 보여주고 나머지는 가려진다. fsbl1/fsbl2는 SD를 가리키는데 ssbl만 eMMC를 가리키는 건 그 자체로 이상한 신호였다.

바로 쓰지 않고 장치별로 직접 봤다.

root@myir:~# lsblk -o NAME,PARTLABEL,SIZE /dev/mmcblk1
mmcblk1p3 ssbl   2M      ← SD카드에도 있다

root@myir:~# lsblk -o NAME,PARTLABEL,SIZE /dev/mmcblk2
mmcblk2p1 ssbl   2M      ← eMMC에도 있다

둘 다 있었다. 라벨만 보고 썼으면 원본 eMMC 부트로더를 덮어썼을 것이다. SD카드 쪽 진짜 경로는 /dev/mmcblk1p3였다.

심볼릭 링크는 편하려고 있는 거지, 보장하려고 있는 게 아니다. 같은 이름이 두 군데 있을 수 있다는 걸 미리 의심하지 않으면, “편한 이름”이 “틀린 대상”을 가리키고 있어도 모르고 지나간다.

경로를 명시해서 진행했다.

# 백업 먼저
dd if=/dev/mmcblk1p3 of=/home/root/ssbl-sd-original-backup.bin

# 크기 확인 (파티션 2MB, 새 바이너리 861KB — 여유 있음)
blockdev --getsize64 /dev/mmcblk1p3

# 쓰기
dd if=/home/root/u-boot.stm32 of=/dev/mmcblk1p3
sync
reboot

작은 것도 하나 걸렸다. 백업 파일을 /root/에 두려다 실패했다.

dd: failed to open '/root/ssbl-sd-original-backup.bin': No such file or directory

이 이미지는 Yocto 기반이라 root의 홈이 /root가 아니라 /home/root다. 이미 printenv에서 HOME=/home/root로 나왔던 걸 지나쳤다가 한 번 더 확인한 셈이다.


재부팅 후

root@myir:~# grep -E "\(PH(12|14|15)\)|\(PB8\)" /sys/kernel/debug/pinctrl/*50002000*/pinconf-pins

PH12/PH14/PH15, PB8 전부 정상으로 돌아왔고, 재부팅 직후부터 LED가 켜지지 않는다. 지난 글에서 남겨뒀던 “짧은 트랜지언트, 전원 인가마다 반복”이 이걸로 사라졌다.


정리

이번 건은 처음부터 끝까지 표면과 실체가 계속 어긋났다.

  1. &ltdc를 껐다고 끝난 게 아니었다 — panel_rgb가 별도로 같은 핀을 잡고 있었다
  2. 빌드 에러 메시지가 가리키는 지점과 진짜 원인이 매번 달랐다 (pkg-config 없음 → 사실은 Kconfig, undefined reference → 사실은 sysroot 오염)
  3. 파티션 라벨을 그대로 믿었으면 eMMC를 덮어썼을 뻔했다

공통점은 하나다. “이름”이나 “가장 눈에 띄는 에러”를 그대로 믿지 않고, 그 뒤에 실제로 뭐가 있는지 한 단계 더 파고들었을 때만 진짜 원인이 나왔다. DTS 작업 때부터 반복돼온 패턴인데, 부트로더와 파티션 레벨까지 내려와서도 똑같았다.

이제 PH12 LED 건은 완전히 닫혔다. 남은 건 QCA7005 PLC 모뎀 바인딩이다.

참고 링크

  1. github 주소 https://github.com/MYiR-Dev/myir-st-u-boot

댓글남기기