이것저것/Windows CE

Windows Embedded CE 6.0 에뮬레이터 startup 코드 분석 #2

우담바라 2007. 4. 24. 20:05

Windows Embedded CE 6.0 에뮬레이터

startup 코드 분석 #2


임베디드 2007/04/11 11:16

출처 :  훌륭한녀석의 끄적거림

계속해서 startup 코드를 살펴보자.
지금부터는 좀 난이도가 있는 코드들이다. MMU 세팅에 관련된 내용으로 ARM Architecture에 대한 기본적인 이해가 필요하다. 자.. 시작해볼까?

        INCLUDE oemaddrtab_cfg.inc
 

        ; Compute physical address of the OEMAddressTable.
20      add     r11, pc, #g_oalAddressTable - (. + 8)
        ldr     r10, =PTs                ; (r10) = 1st level page table


        ; Setup 1st level page table (using section descriptor)    
        ; Fill in first level page table entries to create "un-mapped" regions
        ; from the contents of the MemoryMap array.
        ;
        ;   (r10) = 1st level page table
        ;   (r11) = ptr to MemoryMap array

R11에는 g_oalAddressTable의 시작 엔트리 Address가 들어있게 되고 R10에는 PageTable의 Address(0x30010000)가 들어있다. 다음은 힘들다. 진입!

        add     r10, r10, #0x2000       ; (r10) = ptr to 1st PTE for "unmapped space"
        mov     r0, #0x0E               ; (r0) = PTE for 0: 1MB cachable bufferable
        orr     r0, r0, #0x400          ; set kernel r/w permission
25      mov     r1, r11                 ; (r1) = ptr to MemoryMap array

       
30      ldr     r2, [r1], #4            ; (r2) = virtual address to map Bank at
        ldr     r3, [r1], #4            ; (r3) = physical address to map from
        ldr     r4, [r1], #4            ; (r4) = num MB to map

        cmp     r4, #0                  ; End of table?
        beq     %f40

        ldr     r5, =0x1FF00000
        and     r2, r2, r5              ; VA needs 512MB, 1MB aligned.               

        ldr     r5, =0xFFF00000
        and     r3, r3, r5              ; PA needs 4GB, 1MB aligned.

        add     r2, r10, r2, LSR #18
        add     r0, r0, r3              ; (r0) = PTE for next physical page

35      str     r0, [r2], #4
        add     r0, r0, #0x00100000     ; (r0) = PTE for next physical page
        sub     r4, r4, #1              ; Decrement number of MB left
        cmp     r4, #0
        bne     %b35                    ; Map next MB

        bic     r0, r0, #0xF0000000     ; Clear Section Base Address Field
        bic     r0, r0, #0x0FF00000     ; Clear Section Base Address Field
        b       %b30                    ; Get next element
       
40      tst     r0, #8
        bic     r0, r0, #0x0C           ; clear cachable & bufferable bits in PTE
        add     r10, r10, #0x0800       ; (r10) = ptr to 1st PTE for "unmapped uncached space"
        bne     %b25                    ; go setup PTEs for uncached space
        sub     r10, r10, #0x3000       ; (r10) = restore address of 1st level page table

        ; Setup mmu to map (VA == 0) to (PA == 0x30000000).
        ldr     r0, =PTs                ; PTE entry for VA = 0
        ldr     r1, =0x3000040E         ; uncache/unbuffer/rw, PA base == 0x30000000
        str     r1, [r0]

        ; uncached area.
        add     r0, r0, #0x0800         ; PTE entry for VA = 0x0200.0000 , uncached    
        ldr     r1, =0x30000402         ; uncache/unbuffer/rw, base == 0x30000000
        str     r1, [r0]

먼저 1st page table(또는 first-level descriptor)로 사용될 위치를 계산한다. 여기에서는 0x30010000+0x2000 으로 결국 1st page table의 시작 위치는 0x30012000으로 세팅이 된다(R10 = 0x30012000). 그리고 R0에는 0x40E라는 값을 쓰는데, 이 값은 나중에 설명될 1st level descriptor의 종류로 section으로 사용하는 엔트리라는것을 정하고(0,1번 비트) Cache와 Buffering을 사용하겠다는 의미로 사용된다(2,3번 비트). 마지막으로 10번 비트가 1로 세팅된 것은 Access Permission으로 privileged permission에서는 R/W가능, user permission에서는 No access로 액세스가 금지된다. 그래서 각각의 페이지 테이블 엔트리마다 0x40e 라는 값을 포함시킨다.
R1에 R11의 값을 넣는데 이 R11은 위에 설명되어 있듯이 g_oalAddressTable의 위치로 되어 있다.
이제 g_oalAddressTable에서 데이터를 가져와서 매핑을 해보자. R2에는 virtual address가 들어가고, R3에는 매핑되는 physical address, R4에는 매핑되는 크기(MB단위)가 들어가게 된다. 바로 다음에는 R4가 0인지를 판단해서 0일 경우는 이 루프에서 빠져 나오게 된다(label 40으로 점프).
빠져 나오지 않을 경우에는 아래를 수행한다.
R2에는 512MB의 범위의 Virual Address 영역으로 1MB 단위로 alignment 되는 값을 넣고
R3에는 4GB범위의 Physical address 영역으로 1MB 단위로 alignment 되는 값을 넣는다.
그리고 R2에는 R10과 R2를 shift right 15한 값을 더한 값으로 채운다.
R0에는 0x40e의 값과 Physical address 매핑 값을 더한다.

그리고 R2의 위치(0x30012000)에 R0를 넣는다. 한개 완성
R0에 R0+0x100000(1MB)를 해서 다음 엔트리를 구성한다. 총 R4수만큼..
R0의 상위 12개 비트를 클리어하고 다시 label 30으로 올라간다.

이렇게 해서 g_oalAddressTable에서 세팅된 갯수(그리고 MB수 만큼)만큼 루프를 계속 돈다. 이렇게 해서 만들어지는 테이블이 1st page table이다.

label 40에서는 또 다시 검사를 하는데 R0와 0x8을 AND 연산해서 0이면 Z flag가 1로 세팅 0이 아니면 0으로 세팅된다. 여기까지 오면 0x******40e로 되어 있으므로 0이 아닌 값이 된다. 이 값으로 Z flag는 0으로 세팅되며 이 후에 나타나는 bne에서 다시 label 25로 브랜치 하게 된다. 이 때 R0에는 0x0C로 비트 클리어, R10에는 0x800의 값을 더하게 된다. 이와 같이 하는 이유는 cache와 buffer를 사용하지 않는 virtual memory 한개를 더 만들기 위해서이다. 따라서 0xC로 클리어한 것은 Cache와 버퍼를 쓰지 않고 단지 section format의 descriptor를 만들기 위함이다. 마찬가지로 권한은 동일하다.
해당 페이지 테이블의 위치는 0x30012800이 된다. 결국은 돌고 돌아서 label 40까지 다시 오게 되고  tst에서 z flag가 1로 세팅되어 다음으로 진행하게 된다. 이번에는 R10에다가 0x800을 더하고 0x3000을 더해서 R10을 구한다. 그 값은 0x30012000 -> 0x30012800 -> 0x30013000 -> 0x30010000 일케 된다.
자 이제  바로 다음으로 진입
0x30010000에 0x3000040E라는 값을 쓴다. 이것은 VA 0x0번지를 PA 0x30000000에 매핑하기 위해서이다. 더불어 cache를 사용하고 buffer를 사용하도록한다. (주석은 틀렸다!) 그리고 VA 0x20000000번지를 PA 0x30000000에 매핑하는데 이때에는 cache와 buffer를 사용하지 않도록 매핑한다.

이 다음은 VA 0x30000000을 PA 0x30000000으로 매핑하는데 쓰이는 구문이다. 보자.

        ; Comment:
        ; The following loop is to direct map RAM VA == PA. i.e.
        ;   VA == 0x30XXXXXX => PA == 0x30XXXXXX for S3C2400
        ; Fill in 8 entries to have a direct mapping for DRAM
        ;
        ldr     r10, =PTs               ; restore address of 1st level page table
        ldr     r0,  =PHYBASE

        add     r10, r10, #(0x3000 / 4) ; (r10) = ptr to 1st PTE for 0x30000000

        add     r0, r0, #0x1E           ; 1MB cachable bufferable
        orr     r0, r0, #0x400          ; set kernel r/w permission
        mov     r1, #0
        mov     r3, #64
45      mov     r2, r1                  ; (r2) = virtual address to map Bank at
        cmp     r2, #0x20000000:SHR:BANK_SHIFT
        add     r2, r10, r2, LSL #BANK_SHIFT-18
        strlo   r0, [r2]
        add     r0, r0, #0x00100000     ; (r0) = PTE for next physical page
        subs    r3, r3, #1
        add     r1, r1, #1
        bgt     %b45



r10에는 page table의 초기위치를 넣고, r0에는 ram의 첫 위치를 넣는다. 그리고 VA 0x30000000에 해당하는 page table entry를 찾기 위한 계산을 수행하고 그 값을 r10에 넣는다. 그리고 페이지 테이블 엔트리에 넣을 값을 세팅한다. 이 때 0x41E로 넣는데, 40E와 다른점은 IMPLEMENTATION DEFINED라고 되어 있는데 정확한 의미는 모르겠다(FixMe). 그리고 0x30000000번지부터 매핑해나간다.


        ldr     r10, =PTs               ; (r10) = restore address of 1st level page table

        ; The page tables and exception vectors are setup.
        ; Initialize the MMU and turn it on.
        mov     r1, #1
        mcr     p15, 0, r1, c3, c0, 0   ; setup access to domain 0
        mcr     p15, 0, r10, c2, c0, 0

        mcr     p15, 0, r0, c8, c7, 0   ; flush I+D TLBs
        mov     r1, #0x0071             ; Enable: MMU
        orr     r1, r1, #0x0004         ; Enable the cache

        ldr     r0, =VirtualStart

        cmp     r0, #0                  ; make sure no stall on "mov pc,r0" below
        mcr     p15, 0, r1, c1, c0, 0
        mov     pc, r0                  ;  & jump to new virtual address
        nop



그리고 도메인 세팅하고, page table위치 세팅, MMU세팅하고 VirtualStart를 수행한다. 결국 MMU가 동작하는 상태로 VirtualStart가 수행되고 VirtualStart에서는 스택포인터 세팅(C소스 수행을 위한)을 하고 main으로 진입하면서 끝난다.