펌웨어 설치 스크립트를 읽었다. 이런 것이 반복된다.
flash_erase /dev/mtd0 0x0 0x80
nandwrite -p -s 0x0 /dev/mtd0 ./BOOT.bin
flash_erase 가 먼저 오고 nandwrite 가 뒤에 온다. 지우고 나서 쓴다.
Table of contents
Open Table of contents
지우고 나서 쓰는 이유
일반 파일이면 그냥 덮어쓴다. 플래시는 다르다.
[비트를 1→0] 쓰기로 가능
[비트를 0→1] 지우기로만 가능
쓰기 동작이 비트를 한 방향으로만 바꾼다. 지운 상태가 전부 1이고 쓰기는 그중 일부를 0으로 내리는 일이다.
그래서 새로 쓰려면 먼저 전부 1로 되돌려야 하고 그것이 지우기다. 이미 무언가 쓰여 있는 자리에 그냥 덮으면 옛 값의 0과 새 값이 섞인 결과가 남는다.
지우는 단위가 다르다
지우는 단위가 쓰는 단위보다 크다.
[쓰기] 작은 단위
[지우기] 블록 단위
flash_erase 에 넘기는 길이도 블록 수라 한 바이트를 바꾸려 해도 블록 전체가 지워진다. 파일 시스템이라면 나머지를 어딘가에 담아 뒀다가 되돌려야 하는 일이다.
이 스크립트는 그 과정이 없다. flash_erase 로 영역을 통째로 지우고 nandwrite 로 이미지를 통째로 다시 쓴다.
펌웨어 설치처럼 전부 갈아 끼우는 작업에는 이 쪽이 단순하다. 일부만 고치는 상황이 아예 없으니 읽어 두었다가 되돌릴 것도 없다.
주소가 박혀 있었다
같은 모양이 네 번 반복되는데 주소가 다 다르다.
BOOT.bin /dev/mtd0 0x0 길이 0x80
devicetree.dtb /dev/mtd0 0x1020000 길이 0x1
uImage /dev/mtd0 0x1100000 길이 0x40
rootfs.jffs2 /dev/mtd2 0x0 길이 0x1E0 또는 0x280
파티션 오프셋이 스크립트에 숫자로 그대로 있다. /proc/mtd 를 읽는 것이 아니라 사람이 계산해 적어 넣은 값으로 보인다.
이 배치가 어디서 오는지는 스크립트만 봐서는 안 나온다. 부트로더나 디바이스트리 어딘가에 파티션 표가 있을 것이고 그 표의 값이 여기에 복사돼 온 셈이다.
devicetree.dtb 쪽 정의와 스크립트의 숫자가 같은 정보인데 한쪽만 바뀔 수 있다. 정의는 한 곳이고 사용이 다른 곳인데 둘을 잇는 것이 사람의 기억뿐이다.
주의 — 배치가 어긋날 때
파티션 배치가 바뀌면 어떻게 되는지가 걸린다.
디바이스트리는 새 배치
스크립트는 옛 주소
↓
엉뚱한 곳에 씀
BOOT.bin 이 들어갈 영역을 잘못 지우면 장비가 다시 안 뜬다. 설치 도중에 벽돌이 되는 것이라 되살릴 방법도 현장에서는 마땅치 않다.
/proc/mtd 를 읽어 파티션 이름으로 찾으면 배치가 바뀌어도 따라간다. 그런데 그렇게 안 돼 있는 이유도 짐작은 된다.
runme.sh 같은 설치 스크립트는 한 번 돌고 끝나므로 그 시점의 배치에만 맞으면 된다. 그리고 이름으로 찾는 방식도 이름이 바뀌면 못 찾으니 어느 쪽이든 위험은 남는다.
조건으로 크기가 갈렸다
한 자리에 조건문이 있었다.
if [ -f /dev/mtd3 ]; then
flash_erase /dev/mtd2 0x0 0x1E0
else
flash_erase /dev/mtd2 0x0 0x280
fi
/dev/mtd3 가 있느냐 없느냐로 지우는 크기가 갈린다. 있으면 0x1E0 이고 없으면 0x280 이다.
파티션이 더 잘게 나뉜 쪽은 rootfs 영역이 작고 덜 나뉜 쪽은 크다는 뜻이다. 같은 제품인데 플래시 분할이 다른 판이 있다는 말이 된다.
보드 리비전의 간접 판정
mtd3 의 유무로 판정한다는 것은 하드웨어 리비전을 장치 파일 하나로 추정한다는 뜻이다.
[직접] 리비전을 알려주는 값을 읽음
[간접] 다른 것의 유무로 추정
앞서 본 보드 판정도 ctrl_bd 안의 문자열을 찾는 방식이었다. 여기는 장치 파일의 존재이고 둘 다 간접이다.
리비전을 직접 알려 주는 수단이 이 장비에 없는 것으로 보인다. 있었으면 이렇게 우회할 이유가 없다.
쓰고 나서 원본을 지운다
각 블록 끝에 삭제가 붙어 있다.
nandwrite ... ./BOOT.bin
rm -rf BOOT.bin
처음에는 공간 때문으로 봤다. 장비의 저장 자리가 작으니 uImage 같은 파일을 다 쓰고도 남겨 두면 다음 것을 풀 자리가 없을 수 있다.
그런데 앞의 조건문과 짝을 이루고 있었다.
if [ -e BOOT.bin ]; then
...쓰기...
rm -rf BOOT.bin
fi
존재 확인과 쓰기와 삭제가 한 덩어리다. 그러면 파일이 있다는 것은 아직 안 썼다는 뜻이고 없다는 것은 이미 썼다는 뜻이 된다.
별도 상태 파일 없이 작업 대상의 유무가 진행 상태를 담는다. 상태 파일을 따로 두면 그것과 실제가 어긋날 수 있는데 이 방식은 어긋날 자리가 없다.
다만 쓰다가 전원이 나가면 파일은 남고 플래시는 반쯤 써진 상태가 된다. 다시 돌리면 그 파일을 처음부터 다시 쓰는데 이어서 쓰는 것보다 그 쪽이 맞다.
전체 흐름 — 출력 처리와 중단
모든 명령 끝에 이것이 붙어 있다.
>/dev/null 2>&1
출력과 오류를 전부 버린다. flash_erase 가 실패하든 nandwrite 가 실패하든 화면에는 아무것도 안 남는다.
다만 첫 줄에 이것이 있었다.
#!/bin/sh -e
-e 가 있으니 명령이 실패하면 거기서 중단된다. 무엇이 실패했는지는 모르지만 잘못된 채로 다음 단계까지 가지는 않는다.
절반은 안전한 셈이다. 그래도 화면 대신 파일로 남기면 좋았을 것 같다.
>>/tmp/upgrade.log 2>&1
/tmp/upgrade.log 에 쌓으면 화면은 깨끗하고 기록은 남는다. 업그레이드 실패는 현장이 아니라 멀리서 조사하게 되는데 로그가 없으면 할 수 있는 것이 없다.
정리
- 플래시는
flash_erase로 지우고 나서nandwrite로 써야 한다 - 쓰기는 비트를 1에서 0으로만 바꾸고 되돌리는 것은 지우기다
- 지우기는 블록 단위라 한 바이트를 바꿔도 블록 전체가 대상이다
- 파티션 오프셋이 숫자로 박혀 있어 배치가 바뀌면 어긋난다
/proc/mtd로 찾으면 따라가지만 이름이 바뀌면 그쪽도 못 찾는다/dev/mtd3의 유무로 하드웨어 리비전을 간접 판정한다- 쓴 뒤 원본을 지워서 파일의 유무가 진행 상태가 된다
- 상태 파일이 따로 없으니 상태와 실제가 어긋날 자리가 없다
- 출력을 전부 버려 실패 원인이 안 남는다
sh -e라 중단은 되므로 로그만 파일로 남기면 나았을 것이다