You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6.8 KiB

Firmware OTA 서브시스템 가이드

살아있는 문서(living guide) — 앱 v1.11.10 기준. 코드: src/firmware/*.py. 아키텍처 전체는 architecture.md §3.1.2.

웹 UI 에서 펌웨어 ZIP 을 올려 dpworldapp 의 FW-MMI 채널(TCP 8990)로 플래시한다. 플래시 전 config 백업, 재부팅 후 복원 검사까지 한 흐름으로 오케스트레이션한다. 오케스트레이터는 FirmwareController(단일 인스턴스, thread-safe).


1. staging → flash 흐름

upload(ZIP) → stage(추출·sha256) → preflight(게이트) → flash(백업→전송→commit) → reboot → restore-check
  1. upload — 요청 본문(펌웨어 ZIP)을 디스크 임시파일에 1MB 청크로 스트리밍. 버퍼는 반드시 디스크 백엔드(/opt/fw_upload) — MemoryMax=48M·no-swap 환경에서 tmpfs(/tmp)에 수백 MB 를 버퍼하면 cgroup OOM 으로 서비스가 죽는다.
  2. stage — ZIP 추출 → 확장자로 컴포넌트 자동 식별 → 각 파일 sha256 계산 → /opt/fw_staging. zip-bomb/디스크 채움 가드(컴포넌트당·전체 압축 해제 크기 상한, 중복 파일명 거부, free-space 확인). sidecar(.fw_build_dates.json)에 sha256+build_date 를 증분 기록 → 서비스 재시작 시 _recover_staging 가 sha256 재검증으로 안전 복구(불일치/미기록 파일은 거부·삭제).
  3. preflight — 게이트 체크리스트. critical 전부 통과해야 ok.
  4. flash — 백그라운드 워커가 백업 → 컴포넌트 전송 → commit → reboot 트리거.
  5. restore-check — 재부팅·재접속 후 운영자가 호출 → reseed 감지 시 config 복원 + dpworldapp 재시작.

컴포넌트 식별 (확장자 → wire signature)

확장자 role signature
.rom bootloader BTL
.img kernel KRN
.ext4 rootfs RTF
.dtb dtb DTB

commit 은 별도 message-only 프레임(CPU signature, payload 0).


2. phase stepper (UI 상태)

fw_controller._PHASES — 순서 의미 있음. 각 phase 는 pending/active/done/error, tier 는 na/warn/ok/error 로 매핑.

key label 의미
upload Upload ZIP 수신
verify Verify sha256 계산/검증
preflight Pre-flight 게이트 체크
backup Backup 플래시 전 config 백업
flash Flash 컴포넌트 전송
commit Commit 슬롯 기록·커밋(마지막 컴포넌트 100% 시 시작)
reboot Reboot 장치 재부팅(~10s)
config Config check 재부팅 후 복원 검사

상태머신: idlestagingstagedflashingrebootingdone (실패 시 failed). flashing/staging/rebooting/done 중에는 새 upload/flash 를 거부(race 방지).

preflight 게이트 항목

  • staging(컴포넌트 staged, critical)
  • space(여유공간 headroom 128MB, critical) — staged 바이트는 이미 디스크에 있으므로 중복 계상 안 함
  • fw_port(dpworldapp FW 포트 8990 도달, critical)
  • db(config DB 존재, non-critical — flash 는 되지만 restore-check 에 필요)
  • slot(활성 A/B 슬롯, informational)

3. config 안전 (config_safety)

  • 플래시 전 백업: backup_configdevice_config/protocol_config(+ DB)를 /opt/config_backups/<타임스탬프>/ 로 저장. backup phase 에서 수행.
  • 재부팅 후 복원 검사(detect_and_restore): 펌웨어 flash 가 config 를 factory default 로 reseed 했는지 필드 단위로 감지한다. "reseed" 판정 = 사용자 구별 필드가 대거 factory default 로 되돌아갔고(≥80%, 최소 2개) 새 편집 흔적이 전혀 없을 때(do-no-harm). 이 경우에만 백업본을 단일 트랜잭션(BEGIN IMMEDIATE)으로 원자 복원하고 dpworldapp 을 재시작한다. 애매하면 복원하지 않음(fail-safe toward no-restore).
  • DB 가 읽기 불가/잠금/비어 있어도 500 으로 죽지 않고 사유를 보고한다.
  • restore-check 응답은 정직성 원칙: dpworldapp 재시작이 실제 실패하면 restart_error 를 명시하고 "재시작됨" 으로 거짓 보고하지 않는다(v1.5.4.5 STAB-2).

4. TCP 8990 dpworldapp 채널 (wire protocol)

fw_clientprotocol 프레이밍으로 8990 에 전송한다(server.py: FW_HOST/FW_PORT, 기본 127.0.0.1:8990). dpworldapp 유닛 상태와 무관하게 서빙되는 포트다.

  • 프레임 = ASCII signature + 8바이트 little-endian size + <size> payload. message-only step 은 size 0, payload 없음.
  • ACK: 성공 SUCCESS, 실패 FW_FAIL(deployed firmware behavior 확인). classify_ack 가 success 우선 분류.
  • per-step ACK 타임아웃(ms): BTL/KRN/RTF 20000, DTB 10000, CPU(commit) 120000.

5. A/B rootfs 슬롯 + /opt 영속 (flash 생존)

  • 장치는 A/B rootfs 슬롯(p5=A, p6=B)을 가지며 flash 마다 flip 한다. 활성 슬롯은 /proc/cmdlineroot= 로 best-effort 탐지(_detect_slot). status 에 slot.before/after 노출.
  • commit 성공 후 staging(/opt/fw_staging)은 공간 회수를 위해 삭제(슬롯에 이미 기록됨). config 백업(/opt/config_backups)은 보존.
  • /opt(p12)·/home/root(p11)는 flash 후에도 영속 — 업로드 버퍼·staging·config 백업·DB 가 여기에 있어 flash 를 견딘다. 단, web-configurator/AP 의 systemd 유닛 등 rootfs(/lib) 자산은 flash 마다 wipe 된다(§6).

6. ⚠ flash 생존 caveat

펌웨어 flash 는 rootfs 슬롯을 통째로 교체하므로 rootfs(/lib/systemd 유닛, /usr/bin 스크립트)에 설치된 web-configurator·Wi-Fi AP·network-apply 자산이 매번 wipe 된다. flash 후에는 이들을 재설치해야 한다. 근본 자동화는 BSP 이미지 베이킹이 정답(백로그) — 메모리 web-survives-flash 참조.


7. API

Method Path 비고
GET /api/firmware/status phase stepper + 컴포넌트 진행률 + slot + backup + message/error
POST /api/firmware/preflight 게이트 체크리스트({ok, checks})
POST /api/firmware/upload ZIP 스트리밍 업로드(per-read 소켓 타임아웃) → stage. {ok, components}
POST /api/firmware/flash staged 컴포넌트 플래시 시작. 진행 중이면 409
POST /api/firmware/restore-check reseed 감지 → 복원 + dpworldapp 재시작

오류는 sanitize 되어 내부 fs 경로/예외 내부를 노출하지 않는다(공간부족 507, 잘못된 ZIP 400 등).

API 인증 없음: :9090 은 현재 미인증(문서화된 threat boundary). 운영자 로그인은 설계 백로그 — architecture.md §10.