u-boot

U‑Boot 명령어(Command) 추가 및 실행 구조 완전 정리

임베디드 친구 2025. 12. 7. 20:33
반응형

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 명령은 다음 흐름으로 실행됩니다.

  1. 사용자가 문자열 입력
    • 예: gpio status
  2. common/cli.ccli_simple_process()에서 명령 토큰화
  3. 명령 테이블에서 해당 이름 검색
    • find_cmd()
  4. 연결된 함수(do_XXX) 호출
  5. 반환 값이 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. 결론 요약

이번 글에서 다룬 핵심은 다음과 같습니다.

  1. U‑Boot 명령어는 cmd/ 디렉토리 안에서 관리됨
  2. 명령 추가는 단 하나의 매크로로 가능
    • U_BOOT_CMD(name, maxargs, repeatable, func, usage, help)
  3. 명령 실행은 do_xxx() 구조로 동작
  4. RK3399 기준 예제로 hello, gpio_test 명령을 구현
  5. 환경 변수와 명령은 함께 활용하면 매우 강력함

결론

“U_BOOT_CMD 하나면 나만의 명령어를 만들 수 있다.”

U‑Boot는 단순한 부트로더가 아니라, 하드웨어 bring-up 개발자가 가장 먼저 조작할 수 있는 강력한 디버깅 플랫폼입니다. 필요에 따라 자신만의 명령어를 얼마든지 추가할 수 있으니, 프로젝트 요구에 맞게 적극적으로 활용해 보시기 바랍니다.

반응형