애플리케이션 펌웨어를 검증하고 실행하거나, 새 펌웨어로 갱신하는 작은 프로그램.
메모리 배치
0x08000000 ┌────────────────────┐
│ 부트로더 32KB │ ← 절대 지우지 않는다
0x08008000 ├────────────────────┤
│ 애플리케이션 │ ← 여기를 갱신한다
│ │
0x08080000 └────────────────────┘
부팅 판단 흐름
다이어그램 로딩 중…
점프 코드
void jump_to_app(uint32_t addr) {
uint32_t sp = *(volatile uint32_t *)(addr); // 벡터[0] = 스택
uint32_t pc = *(volatile uint32_t *)(addr + 4); // 벡터[1] = Reset_Handler
// 스택 포인터가 RAM 범위인지 검증 (유효성 확인)
if ((sp & 0x2FFE0000) != 0x20000000) return;
cleanup(); // ★ 아래 참조
SCB->VTOR = addr; // 벡터 테이블 이동
__set_MSP(sp);
((void (*)(void))pc)(); // 점프 (돌아오지 않는다)
}
★ 정리를 빠뜨리면 앱이 죽는다 — 가장 흔한 사고
void cleanup(void) {
HAL_RCC_DeInit(); // 클럭을 기본 상태로
HAL_DeInit(); // 주변장치 정리
SysTick->CTRL = 0; // SysTick 정지
SysTick->LOAD = 0;
SysTick->VAL = 0;
for (int i = 0; i < 8; i++) { // 모든 인터럽트 비활성화
NVIC->ICER[i] = 0xFFFFFFFF;
NVIC->ICPR[i] = 0xFFFFFFFF; // pending 도 클리어
}
__enable_irq(); // 앱은 인터럽트가 켜진 상태를 기대
}
정리하지 않으면
- 부트로더가 켜 둔 UART 인터럽트가 앱 시작 직후 발생
- 앱의 벡터 테이블에 그 핸들러가 없다 (또는 초기화 전 상태)
- Default_Handler 무한 루프 또는 HardFault
"부트로더에서는 되는데 앱으로 넘어가면 죽는다" 의 원인 1순위다.
이미지 검증
typedef struct {
uint32_t magic; // 0xA5A5A5A5
uint32_t version;
uint32_t size;
uint32_t crc32;
uint8_t signature[64]; // 서명 (보안 부트)
} FwHeader;
- ① 매직 넘버 확인 — 펌웨어가 아예 없는 경우를 걸러낸다
- ② 크기 검증 — — 플래시 영역을 넘지 않는가
- ③ CRC 검증 — — 전송·기록 중 손상 확인
- ④ 서명 검증 — — 인증되지 않은 펌웨어 차단 (보안 부트)
④가 없으면 누구나 임의 펌웨어를 넣을 수 있다. IoT 기기 보안에서 필수 요소다.
A/B 뱅크 — 실패해도 되돌아간다
0x08008000 ┌── 뱅크 A (현재 실행 중) ───┐
0x08044000 ├── 뱅크 B (새 펌웨어 기록) ─┤
└────────────────────────────┘
① 실행 중이 아닌 뱅크에 새 펌웨어를 기록
② 검증 성공 → 활성 뱅크 표시를 바꾸고 재부팅
③ 새 펌웨어가 정상 동작을 보고하지 않으면 → 자동으로 이전 뱅크로 롤백
"업데이트 중 전원이 끊겨도 벽돌이 되지 않는다" 는 것이 핵심 가치다. 플래시가 2배 필요하지만, 현장 기기에는 그만한 값어치가 있다.
롤백 판단 — 새 펌웨어가 부팅 후 일정 시간 안에 "정상 동작 확인" 플래그를 쓰지 않으면 워치독 리셋 후 부트로더가 되돌린다.
플래시 쓰기 시 주의
- ① 쓰기 루틴은 RAM에서 실행해야 한다 (같은 뱅크를 지우는 경우)
- ② 쓰기 중에는 인터럽트를 막는 것이 안전하다
- ③ 플래시는 섹터 단위로 지우고, 워드 단위로 쓴다
- ④ 이미 쓴 비트를 1로 되돌리려면 섹터 소거가 필요하다
- ⑤ 쓰기 후 반드시 읽어서 검증
부트로더 진입 조건
- ① 특정 GPIO(버튼)를 누른 채 부팅
- ② 앱이 매직 값을 .noinit RAM 에 쓰고 소프트 리셋
- ③ 앱 이미지가 유효하지 않음
- ④ 통신으로 업데이트 명령 수신 (부팅 후 N초 대기)
②가 가장 실용적이다 — 원격에서 앱이 "업데이트 모드로 재부팅"을 스스로 결정할 수 있다.
부트로더 자체는 업데이트하기 어렵다
부트로더가 자기 자신을 지우다 전원이 끊기면 복구 불가능하다. 그래서 부트로더는 최대한 단순하게 유지하고, 웬만하면 갱신하지 않는다. 필요하면 2단계 부트로더(작은 1차 + 갱신 가능한 2차)를 둔다.