강좌 & 팁
글 수 2,412
2011.07.15 23:00:14 (*.138.143.120)
41345
MMU table 분석.
시스템에서 MMU table 이란 물리적 주소를 가상의 주소로 변환해주는 테이블입니다.
시스템에는 메모리도 있고 IO버스나 레지스터, PCI 버스등 모든 것은 물리적 주소를
갖고 있고 그 주소에 접근함으로써 제어를 할수 있게 됩니다.
MMU table 은 시스템의 물리적 주소를 가상의 주소로 변환함으로써 물리적 주소가 다름에도
OS 에서 동일한 환경을 구축케 할수 있게 해줍니다.
물론 성능의 향상 등도 꾀할수 있다.
32비트 시스템은 4G 의 크기를 갖는 주소시스템을 갖고 있고,
table 의 가장 큰 단위인 1MB 를 기준으로 하면 4096개의 entry 를 갖게 됩니다.
결국 32bit x 4096 = 16KiB 의 크기의 L1 table 이 필요합니다.
커널에서는 swapper_pg_dir 이라는 이름으로 만들어집니다.
좀 무식하지만 해당 영역을 한번 살펴봅니다.
아래의 시스템은 삼성의 S3C6410 기준입니다.
SDRAM 128MB 가 있지만 64MB 만을 커널에서 사용하고
나머지 64MB 는 remap 을 통해서 사용하고 있습니다.
S3C6410 는 CP15 의 c1 register 의 23번 비트가 0 인 경우 ARMv4, ARMv5 MMU 아키텍쳐와 호환됩니다.
현재의 시스템에서는 호환모드를 사용하지 않는 것으로 확인됩니다.
일단 데이타가 0으로 채워진 곳은 표시하지 않았습니다.
데이타가 너무 많고 해당 테이블은 사용하지 않는 영역입니다.
0x00000C00 0x5000040E - DRAM 공간 section
0x00000C01 0x5010040E - DRAM 공간 section
0x00000C02 0x5020040E - DRAM 공간 section
........ 중간생략
0x00000C3D 0x53D0040E - DRAM 공간 section
0x00000C3E 0x53E0040E - DRAM 공간 section
0x00000C3F 0x53F0040E - DRAM 공간 section
0x00000C48 0x53815011 - coarse
0x00000C49 0x53815411 - coarse
0x00000C4A 0x52F6F011 - coarse
0x00000C4B 0x52F6F411 - coarse
0x00000C4C 0x52FA9011 - coarse
0x00000C4D 0x52FA9411 - coarse
0x00000F40 0x504BC041 - coarse
0x00000F41 0x504BC441 - coarse
0x00000F42 0x504BE041 - coarse
0x00000F43 0x504BE441 - coarse
0x00000F44 0x504BF041 - coarse
0x00000F45 0x504BF441 - coarse
0x00000F46 0x77110456 - LCD controller section
0x00000F4C 0x74110456 - Direct Host I/F section
0x00000F50 0x504BD041 - coarse
0x00000F51 0x70010456 - SROM SFR section
0x00000F79 0x7C010456 - USB OTG section
0x00000F7A 0x7C110456 - USB OTG SFR section
0x00000F7B 0x504C0441 - coarse
0x00000F80 0x54010456 - DRAM 공간 section
0x00000F81 0x54110456 - DRAM 공간 section
0x00000F82 0x54210456 - DRAM 공간 section
........ 중간생략
0x00000FBD 0x57D10456 - DRAM 공간 section
0x00000FBE 0x57E10456 - DRAM 공간 section
0x00000FBF 0x57F10456 - DRAM 공간 section
0x00000FF0 0x53833011 - coarse
0x00000FF1 0x53833411 - coarse
0x00000FF2 0x53834011 - coarse
0x00000FF3 0x53834411 - coarse
0x00000FF4 0x53835011 - coarse
0x00000FF5 0x53835411 - coarse
0x00000FF6 0x53836011 - coarse
0x00000FF7 0x53836411 - coarse
0x00000FF8 0x53837011 - coarse
0x00000FF9 0x53837411 - coarse
0x00000FFA 0x53838011 - coarse
0x00000FFB 0x53838411 - coarse
0x00000FFC 0x53839011 - coarse
0x00000FFD 0x53839411 - coarse
0x00000FFE 0x504BB021 - coarse
0x00000FFF 0x504BB421 - coarse
왼쪽은 0 ~ 4095 즉 4G 를 1M 단위 index 입니다.
오른쪽의 테이블이 문제인데요. 가만히 보면 패턴이 있습니다.
먼저 살펴볼 것들은 32비트 데이타중에 0번 1번 bit 입니다.
이 두개의 비트가 section, supersectionf, coarse, fault 를 결정합니다.
00 : fault
01 : coarse
10 : section/supersecton
11 : reserved
자 그럼 위의 데이타에서 0,1 번 비트가 10b 인 것을 찾아 봅니다.
대부분이 메모리이고 Host I/F, USB 등과 같은 덩치 큰 것들은
1MB의 section 으로써 L1 테이블에서 물리공간에 대한 직접 변환이 가능합니다.
대표적으로 아래의 데이타를 분석해 봅니다.
0x00000F4C 0x74110456 - Direct Host I/F section
0-1 비트는 seciton 임을 나타내는 10b 입니다.
2-3 비트는 4가지 속성을 표현합니다,
(write-back cacheable, write-through cacheable, noncached buffered, noncached nonbuffered)
4 비트는 1 입니다.
5-8 비트는 domain 입니다.
9 비트는 0입니다.
10-11 비트는 access pemision 을 표현합니다.
20-31 비트는 1M 단위의 주소입니다.
위의 내용을 기초로 분석해 보면
10b section 임을 표시
01b no cached, buffered
001b domain
01b AP
741H 물리주소의 0x741xxxxx 공간 1MB 단위 offset
쉽게 얘기해서 여러가지 속성을 나타내는 필드들을 제외하고 주소만 놓고 보면,
0xF4C 번째 1MB 영역은 물리주소 0x741 번째 1M 영역으로 매핑된다입니다.
0xF4C00000 - 0xF4CFFFFF == 0x74100000 - 0x741FFFFF 라는 뜻입니다.
실제 코드상에서도 위의 내용을 확인할수 있을까요?
arch/arm/mach-s3c6400/include/mach/map.h 파일에는 아래와 같은 내용이 있습니다.
99 #define S3C64XX_VA_HOSTIFB S3C_ADDR(0x00C00000)
100 #define S3C64XX_PA_HOSTIFB (0x74100000)
101 #define S3C64XX_SZ_HOSTIFB SZ_1M
99번 줄의 매크로는 0xF4000000 을 더해주는 것입니다.
즉... 0xF4C00000 번지부터 1M 를 0x74100000 번지로 매핑시키라는 것입니다.
위에서 찾은 내용과 동일한 내용의 코드를 찾을수 있습니다.
section 을 하나 보았으니 이번엔 coarse 를 하나 살펴봅니다.
0x00000F43 0x504BE441 page
L2 entry 0xc04be400
0x00000000 0x7F006417 small uncached buffered 0xF4300000 0x7F006000
0x00000001 0x7F007417 small uncached buffered 0xF4301000 0x7F007000
0x00000002 0x7F008417 small uncached buffered 0xF4302000 0x7F008000
0x00000003 0x7F009417 small uncached buffered 0xF4303000 0x7F009000
0xF4300000 - 0xF4300FFF 사이에 주소를 접근하면 coarse table 을 참조하게 됩니다.
entry 0x504BE441 에서 coarse page table base 는 [31:10] 이므로
phy -> virt 로 역계산하면 0xC04BE4000 이 됩니다.
해당 주소의 1KB 의 엔트리에 L2 엔트리가 있습니다.
4개이 필드가 표시됩니다.
앞에 표시한 값이 인덱스이고 뒤의 L2 entry를 해석하면 물리주소를 구할수 있습니다.
각 index 는 4KB 단위입니다.
현재의 1M Section index 는 0xF43 입니다.
현재 접근하는 virtual 주소는 0xF4300000
현재 접근하는 physical 주소는 0x7F006000 이 됩니다.
즉 0xF4300000 - 0xF4300FFF : 0x7F006000 - 0x7F006FFF 와 주소가 매핑이 됩니다.
데이타 쉬트를 확인해 보면 해당 주소는 PWM Timer 영역이 되겠습니다.
s3c6410 의 mmu 를 분석하는 코드를 간단히 작성하고 그 결과를 한번 살펴봅니다.
실행은 디바이스 드라이버에서 코드를 넣고 dump_swapper를 호출해 주시면 됩니다.
430 static void dump_coarse_arm1176jzs(unsigned int idx, unsigned int base)
431 {
432 unsigned int *swapper;
433 unsigned int i, loop;
434
435 swapper = (unsigned int *)((base & 0xFFFFFC00) - PHYS_OFFSET + PAGE_OFFSET);
436 printk("\tL2 entry 0x%p\n", swapper);
437
438 loop = SZ_1K /4;
439
440 for(i=0 ; i<loop; i++) {
441 unsigned int address, target, type, c, b;
442
443 address = swapper[i];
444 if( address!=0 ) {
445 c = address & 0x08;
446 b = address & 0x04;
447 type = address & 0x03;
448
449 printk("\t");
450 printk("0x%08X 0x%08X", i, address);
451 printk("\t");
452 switch(type) {
453 case 1: printk("Large"); target = address & 0xFFFF0000; break;
454 case 2:
455 case 3: printk("small"); target = address & 0xFFFFF000; break;
456 default:printk("FAULT\n"); break;
457 }
458 printk("\t");
459 if(c) printk(" cached");
460 else printk("uncached");
461
462 printk("\t");
463 if(b) printk(" buffered");
464 else printk("unbuffered");
465
466 printk("\t");
467 printk("0x%08X", (idx<<20) + (i<<12));
468 printk("\t");
469 printk("0x%08X\n", target);
470 }
471 }
472
473 }
477 static void dump_swapper(void)
478 {
479 unsigned int *swapper = (unsigned int *)0xc0004000;
480 unsigned int i;
481
482 printk("L1 entry 0x%p\n", swapper);
483 for(i=0; i<0x1000; i++) {
484 unsigned int tbl;
485
486 tbl = swapper[i];
487 if(tbl) {
488 unsigned int type = tbl & 0x03;
489
490 printk("0x%08X 0x%08X ", i, swapper[i]);
491 switch(type) {
492 case 0x1: printk("page\n"); dump_coarse_arm1176jzs(i, tbl); break;
493 case 0x2:
494 {
495 if(tbl & (1<<18)) printk("Supersection\n");
496 else printk("Section\n");
497 break;
498 }
499 default:printk("FAULT\n"); break;
500 }
501 }
502 }
503 }
결과는 너무 길어서 생략하고요... 한번 돌려보시면 결과를 얻을수 있습니다.