Skip to content
isdnetworks
Go back

쓰기 전에 지워야 한다

펌웨어 설치 스크립트를 읽었다. 이런 것이 반복된다.

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 에 쌓으면 화면은 깨끗하고 기록은 남는다. 업그레이드 실패는 현장이 아니라 멀리서 조사하게 되는데 로그가 없으면 할 수 있는 것이 없다.

정리


Share this post on:

Previous Post
하나 고쳤더니 다음 것이 나왔다
Next Post
한 대만 보고 판단했다