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 }
            결과는 너무 길어서 생략하고요...  한번 돌려보시면 결과를 얻을수 있습니다.