[FS/mtd] mtd 관련 작업
embedded system에서 많이 사용하는 NAND등의 flash를 사용할때 많이 쓰는
mtd 영역에 대해서 작업시 활용할 수 있는 Tip을 살펴본다.
- mtd 정보확인
root@Embedded:/# cat /proc/mtd
dev: size erasesize name
mtd0: 00040000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00630000 00010000 "rootfs"
mtd3: 00070000 00010000 "rootfs_data"
mtd4: 00150000 00010000 "kernel"
mtd5: 00010000 00010000 "otp"
mtd6: 00010000 00010000 "nvram"
mtd7: 00010000 00010000 "art"
mtd8: 00780000 00010000 "firmware"
root@Embedded:/#
참고> mtdX vs. mtdblockX
root@Embedded:/proc# cat /proc/devices
Character devices:
1 mem
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
10 misc
90 mtd
108 ppp
128 ptm
136 pts
180 usb
189 usb_device
254 ttyATH
Block devices:
259 blkext
31 mtdblock
root@Embedded:/dev# ls -l mtd7 mtdblock7
crw-r--r-- 1 root root 90, 14 Jan 1 1970 mtd7
brw-r--r-- 1 root root 31, 7 Jan 1 1970 mtdblock7
mtdX : Charactor Device
flash 관련 tool 들을 사용
mtdblockX : Block Device
일반 파일 관련 툴들을 사용
(MTD Write시 사용하지 말라는 언급이 있음.
http://www.at91.com/viewtopic.php?t=21193
DO NOT use dd with NAND partitions !Please use this instead
- Read / Write
일반 파일들 처럼 cp cat 등 등 활용하면 됨. ( 쓰기는 잘 안되는 경우들이 많은데, 케이스 별로 원인을 찾아서 대응해야 함.)
root@Embedded:/# hexdump -C /dev/mtdblock7 -n 6 -s 4098 | head -1
00001002 04 f0 21 11 11 12
root@Embedded:/# tftp -p -l /dev/mtdblock7 -r firstpw/mtdblock7 192.168.88.203
- mtd 영역 Hex 변환
위의 Read의 hexdump 외에도 xxd 등을 활용하여 hexdump , 변경(bin -> txt(dump), txt(dump) -> bin)을 수행 할 수 있다.
to hexdump>
xxd mtdblock7 > mtdblock7.xxd
- openWrt 에 mtd-util 추가하기
mtd-utils :
http://processors.wiki.ti.com/index.php/MTD_Utilities
tmp/.config-target.in
select DEFAULT_mtd
위 처럼 되어 있는 곳마다 아래 라인 추가
select DEFAULT_mtd-utils
busybox's flash tools
base system > busybox > miscellaneous Utilities
x x [*] flashcp
x x [*] flash_lock
x x [*] flash_unlock
x x [*] flash_eraseall
- mtd : Permission Error
Read only 인지 확인해보라
대부분 해당 Flag는 Bootloader에서 Command param으로 kernel로 전송한다 (ro)
변경 방법 1>
Bootloader의 CFG에서 boot argment 를 변경
변경 방법 2> 방법 1을 사용할 수 없는 상황
Kernel 소스에서 Flag 강제 세팅 : 아래 처럼 코드 Blocking
drivers/mtd/cmdlinepart.c
static struct mtd_partition * newpart(char *s,
char **retptr,
int *num_parts,
int this_part,
unsigned char **extra_mem_ptr,
int extra_mem_size)
{
........
/* test for options */
if (strncmp(s, "ro", 2) == 0)
{
// mask_flags |= MTD_WRITEABLE;
s += 2;
}
# Boot Param으로 넘어오지 않는 경우에는 Kernel에서 직접 Driver 설정 화일 변경
아래 위치에서 static struct mtd_partition 설정 부분을 확인해보길 .... ( grep으로 ".mask_flags"를 찾는 것도 한 방법)
drivers/mtd/ ....
arch/mips/.....
mtd info program source
root@16M:/etc/firstpw# ./mtds
[/dev/mtd0] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(040000| 262144) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd1] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(010000| 65536) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd2] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(630000| 6488064) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd3] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(070000| 458752) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd4] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(150000| 1376256) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd5] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(010000| 65536) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd6] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(010000| 65536) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd7] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(010000| 65536) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
[/dev/mtd8] MTD_NORFLASH WR BitWR ERASE UnLOCK Sz(780000| 7864320) ErSz(65536) WrSz(1) OobSz(0) Pad(4197963)
root@16M:/etc/firstpw#
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define TARGET_MTD "/dev/mtd7"
int exit_w_err(char* pTitle, int ret)
{
printf("[%s] Ret[%d]\n", pTitle, ret);
printf(" MTD Error: %d[%s]\n", errno, strerror(errno));
_exit(0);
}
#define CASE_STR(_tag) \
case _tag: return #_tag;
char* str_type(__u8 type)
{
switch(type) {
CASE_STR(MTD_ABSENT)
CASE_STR(MTD_RAM)
CASE_STR(MTD_ROM)
CASE_STR(MTD_NORFLASH)
CASE_STR(MTD_NANDFLASH)
CASE_STR(MTD_DATAFLASH)
CASE_STR(MTD_UBIVOLUME)
CASE_STR(MTD_MLCNANDFLASH)
default: break;
}
return "Unknown type";
}
int print_mtd_info(char* pTitle, mtd_info_t* pInfo)
{
char sbuff[1024];
int len = 0;
len += snprintf(sbuff + len, sizeof(sbuff) - len, "[%s] %13s", pTitle, str_type(pInfo->type));
if(pInfo->flags & MTD_WRITEABLE) {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " WR");
} else {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " RO");
}
if(pInfo->flags & MTD_BIT_WRITEABLE) {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " BitWR");
} else {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " ");
}
if(pInfo->flags & MTD_NO_ERASE) {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " NoERA");
} else {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " ERASE");
}
if(pInfo->flags & MTD_POWERUP_LOCK) {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " PwrLOCK");
} else {
len += snprintf(sbuff + len, sizeof(sbuff) - len, " UnLOCK ");
}
len += snprintf(sbuff + len, sizeof(sbuff) - len, " Sz(%06x|%8u)", pInfo->size, pInfo->size);
len += snprintf(sbuff + len, sizeof(sbuff) - len, " ErSz(%5u)", pInfo->erasesize);
len += snprintf(sbuff + len, sizeof(sbuff) - len, " WrSz(%1u)", pInfo->writesize);
len += snprintf(sbuff + len, sizeof(sbuff) - len, " OobSz(%1u)", pInfo->oobsize);
len += snprintf(sbuff + len, sizeof(sbuff) - len, " Pad(%lu)", pInfo->padding);
len += snprintf(sbuff + len, sizeof(sbuff) - len, "\n");
printf(sbuff);
return 1;
}
#if 1 /* firstpw.2019.0110 */
#else /* firstpw.2019.0110 */
struct mtd_info_user {
__u8 type;
__u32 flags;
__u32 size;
__u32 erasesize;
__u32 writesize;
__u32 oobsize;
__u64 padding;
};
#endif /* firstpw.2019.0110 */
int main(int argc, char *argv[])
{
mtd_info_t mtd_info; // the MTD structure
erase_info_t ei; // the erase block structure
int fd;
int i;
int ret;
char sbuff[256];
unsigned char data[20] = { 0xDE, 0xAD, 0xBE, 0xEF, // our data to write
0xDE, 0xAD, 0xBE, 0xEF,
0xDE, 0xAD, 0xBE, 0xEF,
0xDE, 0xAD, 0xBE, 0xEF,
0xDE, 0xAD, 0xBE, 0xEF};
unsigned char read_buf[20] = {0x00}; // empty array for reading
for( i = 0; i < 9; i++) {
sprintf(sbuff, "/dev/mtd%d", i);
fd = open(sbuff, O_RDONLY);
if(fd < 0) {
printf("O_RDONLY> MTD Error: %d[%s]\n", errno, strerror(errno));
continue;
}
ret = ioctl(fd, MEMGETINFO, &mtd_info);
if(ret < 0) {
printf("ioctl> MTD Error: %d[%s]\n", errno, strerror(errno));
continue;
}
print_mtd_info(sbuff, &mtd_info);
close(fd);
}
_exit(0);
for( i = 0; i < 9; i++) {
sprintf(sbuff, "/dev/mtd%d", i);
fd = open(sbuff, O_RDONLY);
printf("O_RDONLY> [%s] Ret[%d]\n", sbuff, fd);
if(fd < 0) {
printf("O_RDONLY> MTD Error: %d[%s]\n", errno, strerror(errno));
} else {
close(fd);
}
fd = open(sbuff, O_WRONLY);
printf("O_WRONLY> [%s] Ret[%d]\n", sbuff, fd);
if(fd < 0) {
printf("O_WRONLY> MTD Error: %d[%s]\n", errno, strerror(errno));
} else {
close(fd);
}
fd = open(sbuff, O_RDWR);
printf("O_RDWR> [%s] Ret[%d]\n", sbuff, fd);
if(fd < 0) {
printf("O_RDWR> MTD Error: %d[%s]\n", errno, strerror(errno));
} else {
close(fd);
}
}
_exit(0);
fd = open(TARGET_MTD, O_RDWR); // open the mtd device for reading and
// writing. Note you want mtd0 not mtdblock0
// also you probably need to open permissions
// to the dev (sudo chmod 777 /dev/mtd0)
if(fd < 0) {
exit_w_err("Open " TARGET_MTD, fd);
}
ret = lseek(fd, 0x1000, SEEK_SET); // go to the first block
if(ret < 0) {
exit_w_err("lseek[0x1000] " TARGET_MTD, ret);
}
ret = read(fd, read_buf, sizeof(read_buf)); // read 20 bytes
if(ret < 0) {
exit_w_err("read[0x1000] " TARGET_MTD, ret);
}
// sanity check, should be all 0xFF if erase worked
for(i = 0; i<20; i++)
printf("Before buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
ret = ioctl(fd, MEMGETINFO, &mtd_info); // get the device info
if(ret < 0) {
exit_w_err("ioctl[MEMGETINFO] " TARGET_MTD, ret);
}
// dump it for a sanity check, should match what's in /proc/mtd
printf("MTD Type: %x\nMTD total size: %x bytes\nMTD erase size: %x bytes\n",
mtd_info.type, mtd_info.size, mtd_info.erasesize);
ei.length = mtd_info.erasesize; //set the erase block size
for(ei.start = 0; ei.start < mtd_info.size; ei.start += ei.length)
{
ret = ioctl(fd, MEMUNLOCK, &ei);
if(ret < 0) {
exit_w_err("ioctl[MEMUNLOCK] " TARGET_MTD, ret);
}
printf("Eraseing Block %#x\n", ei.start); // show the blocks erasing
// warning, this prints a lot!
ret = ioctl(fd, MEMERASE, &ei);
if(ret < 0) {
exit_w_err("ioctl[MEMERASE] " TARGET_MTD, ret);
}
}
ret = lseek(fd, 0, SEEK_SET); // go to the first block
if(ret < 0) {
exit_w_err("lseek[0x1000] " TARGET_MTD, ret);
}
ret = read(fd, read_buf, sizeof(read_buf)); // read 20 bytes
if(ret < 0) {
exit_w_err("read[0x1000] " TARGET_MTD, ret);
}
// sanity check, should be all 0xFF if erase worked
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
ret = lseek(fd, 0, SEEK_SET); // go back to first block's start
if(ret < 0) {
exit_w_err("lseek[0x1000] " TARGET_MTD, ret);
}
ret = write(fd, data, sizeof(data)); // write our message
if(ret < 0) {
exit_w_err("write[0x1000] " TARGET_MTD, ret);
}
ret = lseek(fd, 0, SEEK_SET); // go back to first block's start
if(ret < 0) {
exit_w_err("lseek[0x1000] " TARGET_MTD, ret);
}
ret = read(fd, read_buf, sizeof(read_buf));// read the data
if(ret < 0) {
exit_w_err("read[0x1000] " TARGET_MTD, ret);
}
// sanity check, now you see the message we wrote!
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
close(fd);
return 0;
}
'프로...Linux' 카테고리의 다른 글
[Util] sysdig - 사용자 감시 (0) | 2019.01.15 |
---|---|
[TS/Make] "aclocal-1.11: command not found" (0) | 2019.01.14 |
[Info] Check HW information (0) | 2018.12.21 |
[SCRIPT] partitioning (0) | 2018.12.14 |
[util] partition/mount - fdisk/lsblk(blkid)/df/mount (0) | 2018.12.14 |