U‑Boot 명령어(Command) 추가 및 실행 구조 완전 정리
Embedded Linux 시스템에서 U‑Boot는 부팅 초기에 가장 먼저 실행되는 부트로더이며, 디버깅과 시스템 bring-up 과정에서 매우 중요한 역할을 합니다. 특히 개발자는 자신만의 명령을 추가하여 디버깅, 하드웨어 제어, 테스트 자동화 등을 수행하게 됩니다. 본 포스팅에서는 Rockchip RK3399 보드를 기준으로 U-Boot 명령 추가 방식, U_BOOT_CMD 매크로 구조, 명령 파싱/실행 과정, 사용자 정의 명령 예제, 환경 변수 연동 방법까지 체계적으로 정리합니다.
1. cmd/ 디렉토리 구조
U‑Boot 소스 트리에서 명령 관련 파일은 모든 보드가 공유하는 공통 명령이기 때문에 cmd/ 디렉토리에 위치합니다.
u-boot/
├── cmd/
│ ├── cmd_boot.c
│ ├── cmd_gpio.c
│ ├── cmd_i2c.c
│ ├── cmd_mem.c
│ ├── cmd_nvedit.c ← 환경 변수 관련 명령
│ ├── cmd_mm.c ← 메모리 접근 명령
│ ├── cmd_rockusb.c ← Rockchip USB Download 명령
│ ├── ...
각 파일은 다음과 같은 형태로 명령을 정의합니다.
- 함수 구현 (ex:
do_gpio(),do_ext4_ls()) - 명령 등록 매크로 (
U_BOOT_CMD())
즉, cmd/*.c 하나는 "하나의 명령" 또는 "관련된 여러 명령"을 담당합니다.
2. U_BOOT_CMD 매크로를 이용한 명령 등록 방법
U‑Boot의 모든 명령은 다음 매크로로 등록됩니다.
U_BOOT_CMD(name, maxargs, repeatable, cmd_func, usage, help)
각 인자의 의미는 다음과 같습니다.
| 인자 | 설명 |
|---|---|
| name | 부트 명령어 문자열 (예: "gpio") |
| maxargs | 인수 개수 제한 |
| repeatable | 반복 실행 가능 여부 (0 또는 1) |
| cmd_func | 실제 명령 실행 함수 포인터 |
| usage | "help" 명령 시 1줄 요약 |
| help | 상세 help 문자열 |
예시: cmd_gpio.c 내부 (U‑Boot 기본 제공)
U_BOOT_CMD(
gpio, 3, 0, do_gpio,
"GPIO sub-system",
"status - show GPIO status\n"
"gpio set N - set GPIO N"
);
이 구조 그대로 개발자가 원하는 명령을 추가할 수 있습니다.
3. 명령 파싱 및 실행 과정 (do_ 함수 구조)
U‑Boot 명령은 다음 흐름으로 실행됩니다.
- 사용자가 문자열 입력
- 예:
gpio status
- 예:
- common/cli.c →
cli_simple_process()에서 명령 토큰화 - 명령 테이블에서 해당 이름 검색
find_cmd()
- 연결된 함수(do_XXX) 호출
- 반환 값이 0이면 성공, 음수면 실패
즉, 핵심은 do_xxxx() 함수입니다.
do 함수 형태
static int do_xxx(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
- argc / argv는 표준 C main()과 동일
flag는 반복 실행 관련 정보- 반환값 0 = 성공, 1 또는 음수 = 실패
4. RK3399 기준 사용자 정의 명령 추가 예제
이제 실제로 RK3399 보드에 “hello” 명령과 “gpio_test” 명령을 추가해보겠습니다.
4‑1. hello 명령 추가
파일 생성
cmd/cmd_hello.c 파일을 새로 생성합니다.
#include <common.h>
static int do_hello(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
printf("Hello, RK3399!\n");
return 0;
}
U_BOOT_CMD(
hello, 1, 0, do_hello,
"Print hello message",
"Simply prints 'Hello, RK3399!'"
);
빌드 반영
cmd/Kconfig에 옵션 추가하거나, 단순히 파일을 Makefile에 추가합니다.
obj-y += cmd_hello.o
빌드 후 U‑Boot 콘솔에서 테스트합니다.
=> hello
Hello, RK3399!
4‑2. gpio_test 명령 (실제 핀 제어)
RK3399에서는 U‑Boot에서 GPIO 제어가 가능합니다. 예제로 GPIO2_A0 핀을 HIGH로 출력하는 명령을 만들어보겠습니다.
#include <common.h>
#include <asm/gpio.h>
static int do_gpio_test(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
int pin = GPIO2_A0;
gpio_request(pin, "gpio_test");
gpio_direction_output(pin, 1);
printf("GPIO2_A0 set to HIGH\n");
return 0;
}
U_BOOT_CMD(
gpio_test, 1, 0, do_gpio_test,
"Test GPIO output on RK3399",
"No arguments. Sets GPIO2_A0 HIGH."
);
빌드 후 실행 결과:
=> gpio_test
GPIO2_A0 set to HIGH
5. 환경 변수와 커맨드의 상호 작용
U‑Boot의 강점 중 하나는 환경 변수(env) 입니다.
명령과 env는 서로 연동하여 유연한 스크립트를 만들 수 있습니다.
5‑1. env에서 값 읽기
const char *val = env_get("bootmode");
5‑2. env 값을 사용해 동작 변경 예제
예: hello 명령에 조건문 추가
// bootmode 값이 'debug'이면 추가 메시지 출력
const char *mode = env_get("bootmode");
if (mode && !strcmp(mode, "debug"))
printf("[DEBUG MODE] Extra info...\n");
5‑3. U‑Boot 스크립트에서 사용자 명령 호출
setenv bootmode debug
hello
또는 자동 실행 스크립트에 추가:
setenv bootcmd "gpio_test; boot_linux"
saveenv
부팅 시 자동으로 사용자 명령이 실행됩니다.
6. 결론 요약
이번 글에서 다룬 핵심은 다음과 같습니다.
- U‑Boot 명령어는 cmd/ 디렉토리 안에서 관리됨
- 명령 추가는 단 하나의 매크로로 가능
- U_BOOT_CMD(name, maxargs, repeatable, func, usage, help)
- 명령 실행은
do_xxx()구조로 동작 - RK3399 기준 예제로 hello, gpio_test 명령을 구현
- 환경 변수와 명령은 함께 활용하면 매우 강력함
결론
“U_BOOT_CMD 하나면 나만의 명령어를 만들 수 있다.”
U‑Boot는 단순한 부트로더가 아니라, 하드웨어 bring-up 개발자가 가장 먼저 조작할 수 있는 강력한 디버깅 플랫폼입니다. 필요에 따라 자신만의 명령어를 얼마든지 추가할 수 있으니, 프로젝트 요구에 맞게 적극적으로 활용해 보시기 바랍니다.
'u-boot' 카테고리의 다른 글
| U-Boot에서 커널 및 루트파일시스템 로딩 (RK3399 기준) (0) | 2025.12.09 |
|---|---|
| U-Boot 환경 변수와 스크립트 완전 정복(Rockchip RK3399 기반) (0) | 2025.12.08 |
| U-Boot 드라이버 구조와 포팅 방법 (0) | 2025.12.05 |
| U-Boot 부팅 시퀀스 완전 해부 (0) | 2025.12.04 |
| U-Boot 빌드 및 보드 포팅 준비 (0) | 2025.12.03 |