compressed/head.S – cache_clean_flush:

캐시를 모두(d-cache, i-cache) flush(clean & invalidate) 한다. 아키텍처마다 캐시를 flush하는 구현이 각각 다르기 때문에 현재 동작하는 CPU 아키텍처코드를 알아내어 해당 아키텍처에서 구현해놓은 cache flush 루틴을 수행하게 한다. 아래 순서도는 ARMv7에 대응하는 cache flush 루틴의 순서도이다.

decompress_head.s분석7

 

Flush & Clean

Data 캐시에서 flush와 Clean이라는 용어는 여러 아키텍처에 따라 각각 의미가 다르게 해석되어 사용되므로 유의해야 한다.

  • flush는 arm에 해당하는 clean, invalidate, clean & invalidate의 3가지 의미로 혼용 사용된다.
  • clean은 arm에 해당하는 clean, clean & invalidate의 2가지 의미로 혼용 사용된다.

따라서 arm 매뉴얼에서는 flush라는 용어를 가급적 사용하지 않고 clean, invalidate, clean & invalidate의 3가지로 정확히 나누어 사용한다.

  • clean
    • dirty(기록) 설정된 캐시 라인 -> 메모리로 기록하고 캐시에서 그대로 유지
      • dirty를 0으로 변경, valid는 1로 유지
  • invalidate
    • dirty 유무와 상관 없이 캐시 라인을 버린다. (캐시 라인은 메모리에 기록하지 않는다)
      • valid를 0으로 변경
  • clean & invalidate
    • dirty(기록) 설정된 캐시 라인이 있는 경우 메모리로 기록하고 캐시 라인을 버린다.
      • dirty 및 valid를 0으로 변경
    • dirty(기록) 설정되지 않은 캐시 라인은 곧바로 캐시 라인을 버린다.
      • valid를 0으로 변경

Instruction 캐시, Branch Pridict 캐시, TLB 캐시 등은 Dirty 비트가 없으므로 flush, clean 및 invalidate 모두 같은 의미로 사용된다.

  • arm 매뉴얼에서는 invalidate를 표준으로 사용하고 flush 라는 용어도 사용된다.

 

cache_clean_flush:

/*
 * Clean and flush the cache to maintain consistency.
 *
 * On exit,
 *  r1, r2, r3, r9, r10, r11, r12 corrupted
 * This routine must preserve:
 *  r4, r6, r7, r8
 */
                .align  5
cache_clean_flush:
                mov     r3, #16
                b       call_cache_fn
  • 캐시 clean & flush를 수행한다.
  • rpi2: ARMv7 아키텍처를 사용하므로 __armv7_mmu_cache_flush로 진행

 

__armv7_mmu_cache_flush:

__armv7_mmu_cache_flush:
                tst     r4, #1
                bne     iflush
                mrc     p15, 0, r10, c0, c1, 5  @ read ID_MMFR1
                tst     r10, #0xf << 16         @ hierarchical cache (ARMv7)
                mov     r10, #0
                beq     hierarchical
                mcr     p15, 0, r10, c7, c14, 0 @ clean+invalidate D
                b       iflush
  • tst r4, #1
    • r4에는 decompressed 커널 시작 주소가 담겨있는데 임시로 하위 1비트를 캐시 on 유보 비트로 이용하고 있다.
    • 캐시 유보 비트가 설정되어 있으면 d-cache를 사용하지 않았으모로 d-cache에 대해 flush 하지 않고 i-cache만 flush하도록 iflush: 루틴으로 이동한다.
  • mrc p15, 0, r10, c0, c1, 5
    • ID.MMFR1.L1_Harvard_cache 설정 상태를 읽어온다.
      • ARMv7은 hierarchical 캐시를 사용하므로 값이 0으로 설정되어 있다.
  • mov r10, #0
    • 아래 mcr 명령을 수행할 일이 있는 경우 register dependency 기법을 이용하여 CPU의 in-order execution을 보장하게 한다.
  • beq hierarchical
    • ARMv7 의 모든 캐시들은 hierarchical 캐시를 사용하는 것으로 알려져 있다. 따라서 모두 이루틴으로 진입한다.
    • 2015년 1월 22일 이 부분의 패치가 제출되어 있는 상태로 아직 메인 스트림에 반영되지 않고 있다.
  • mcr p15, 0, r10, c7, c14, 0
    • clean & invalidate d-cache all을 수행하는 명령이지만 ARMv7은 모두 hierarchical 캐시를 사용하므로 이 루틴으로 들어올 일이 없다.

 

hierarchical:

  • 캐시 레벨 관리
    • ARMv7 이전의 아키텍처는 직접적으로 관리하는  캐시가 L1 밖에 없었다.
    • ARMv7 아키텍처가 만들어지면서 다중 캐시를 지원하게 되었다.
      • 캐시는 Level 1부터 Level 7까지의 정보로 구성될 수 있는데 아키텍처나 SoC를 제조하는 회사마다 캐시의 구성방법과 LoC(Level of Cache Coherency)가 모두 다르다.
  • 캐시 flush 방법
    • ARMv7 이전의 아키텍처
      • L1 i-cache는 한 번의 명령으로 삭제 가능
      • L1 d-cache는 한 번의 명령으로 삭제 가능
    • ARMv7 아키텍처
      • 각 레벨별로 i-cache를 한 번의 명령으로 삭제 가능
      • 각 레벨별로 d-cache를 한 번의 명령으로 삭제 불가능
        • 모든 캐시 레벨에 대해 set/way 방식으로 하나씩 지워나간다.
        • 각 cache line 하나에 대해 한 번의 명령으로 삭제하므로 모두를 지우기 까지 상당한 overhead가 소모되는 것이 단점이다. (최대 수 ms 시간 소모)
hierarchical:
                mcr     p15, 0, r10, c7, c10, 5 @ DMB
                stmfd   sp!, {r0-r7, r9-r11}
                mrc     p15, 1, r0, c0, c0, 1   @ read clidr
                ands    r3, r0, #0x7000000      @ extract loc from clidr
                mov     r3, r3, lsr #23         @ left align loc bit field
                beq     finished                @ if loc is 0, then no need to clean
  • mcr p15, 0, r10, c7, c10, 5
    • 캐시를 set/way 방식으로 하나씩 지워나가기 전에 DMB (Data Memory Barrier operation)를 사용하여 이미 사용중인 memory 작업이 완료할 때까지 기다린다.
  • stmfd sp!, {r0-r7, r9-r11}
    • 이 루틴이 동작하는 동안 레지스터들을 보호하기 대부분의 레지스터들을 백업해둔다.
  •  mrc   p15, 1, r0, c0, c0, 1
    • CLIDR(Cache Level ID Register)를 사용하여 캐시 레벨 정보를 읽어온다.
  • ands r3, r0, #0x7000000
    • LoC가 담긴 비트들만 and 연산으로 확보한다.
    • pi2: LoC=2 (Level 2 Coherency를 가지고 있다.)
  • mov r3, r3, lsr #23
    • 잃어온 LoC 값을 24비트 우측으로 쉬프트하지 않고 23비트만큼만 우측으로 쉬프트한다.
    • r3 = LoC x 2와 동일하다.
      • rpi2:  r3=4
  • beq finished
    • LoC가 0이면 finish로 빠져나간다.
                mov     r10, #0                 @ start clean at cache level 0
loop1:
                add     r2, r10, r10, lsr #1    @ work out 3x current cache level
                mov     r1, r0, lsr r2          @ extract cache type bits from clidr
                and     r1, r1, #7              @ mask of the bits for current cache only
                cmp     r1, #2                  @ see what cache we have at this level
                blt     skip                    @ skip if no cache, or just i-cache
  • mov r10, #0
    • 가장 바깥쪽 루프(loop1) 카운터로 사용되는 r10(캐시 레벨)을 0으로 설정한다.
      • 이 값은 2씩 증가하여 r3(LoC x 2)이 될 때까지 증가한다.
      • rpi2: 0(L1 캐시 삭제), 2(L2 캐시 삭제),  4(루프 종료)
  • add     r2, r10, r10, lsr #1
    • r2: 해당 캐시레벨의 타입을 가져오기 위해 3bit씩 위치를 곱한다.
    • 0, 3, 6, 9, …씩 증가
  • mov     r1, r0, lsr r2
    • r1 = r0(CLIDR) 값을 r2만큼 우측으로 쉬프트하면 해당 캐시 레벨 타입 정보가 있는 곳까지 쉬프트한다.
  • and r1, r1, #7
    • 캐시 타입 정보 비트는 3비트로 구성되어 있다.
  • cmp r1, #2
    • r1: 캐시 타입 정보
      • 0=0=no cache
      • 1=instrunction cache only
      • 2=data cache only
      • 3=seperate inst & data cache
      • 4=unified cache (inst + data)
      • 5~7=reserved
  • blt skip
    • r1(현재 캐시 레벨이 지원하는 캐시 타입)이 2보다 작으면 d-cache가 없으므로 skip으로 이동
    • d-cache에서만 루프에서 flush 작업을 수행할 예정이다.

 

                mcr     p15, 2, r10, c0, c0, 0  @ select current cache level in cssr 
                mcr     p15, 0, r10, c7, c5, 4  @ isb to sych the new cssr&csidr
                mrc     p15, 1, r1, c0, c0, 0   @ read the new csidr
                and     r2, r1, #7              @ extract the length of the cache lines
                add     r2, r2, #4              @ add 4 (line length offset)
                ldr     r4, =0x3ff
                ands    r4, r4, r1, lsr #3      @ find maximum number on the way size
                clz     r5, r4                  @ find bit position of way size increment
                ldr     r7, =0x7fff
                ands    r7, r7, r1, lsr #13     @ extract max number of the index size
  • mcr p15, 2, r10, c0, c0, 0
    • r1: CCSIDR을 읽어온다.
    • 선택된 캐시의 정보가 담김.
      • WT(Write Through) 지원 여부 bit
      • WB (Write Back) 지원 여부 bit
      • RA(Read Allocation) 지원 여부 bit
      • WA(Write Allocation) 지원 여부 bit
      • LineSize: cache line 바이트 수
        • 1=8 words, 2=16 bytes, 3=32 words, …
        • rpi2: 2 (L1 & L2 data cache line size = 16 words)
      • Associativity
        • way 수 – 1
        • rpi2: L1 i-cache=1(2 way), L1 d-cache=3(4 way), L2 cache=7(8 way)
      • Numsets
        • Set(index) 수 – 1
        • rpi2: L1 i-cache=0x1ff, L1 d-cache=0x7f, L2 d-cache=0x3ff
  • and r2, r1, #7
    • cache line 정보 비트를 읽어온다.
  • add r2, r2, #4
    • r2 += 4를 취한다.
    • 5=8 words, 6=16 words, 7=32 words, …
    • rp2: 6
  • ldr r4, =0x3ff
    • associativity(way – 1) 추출을 위한 비트마스크를 0x3ff로 한다.
  • ands r4, r4, r1, lsr #3
    • r4 = loop2용 max associativity(way – 1) 값
    • r1(CLIDR)값을 우측으로 3 쉬프트하여 associativity(way – 1) 값 만을 읽어온다.
    • rpi2: L1 d-cache=0x7f, L2 d-cache=0x3ff
  • clz r5, r4
    • r5: clz: MSB(최상위 비트)부터 시작하여 비트가 0인 갯수를 알아낸다.
      • 예) 0b 00000000 00000000 00000000 00000011 -> 30
  • ldr r7, =0x7fff
    • NumSets(index-1) 추출을 위한 비트마스크를 0x7fff로 한다.
  • ands r7, r7, r1, lsr #13
    • r7: loop2용 NumSets(index-1) 카운터
      • for (r7 = Max NumSets(r1에서 추출) ; r7 >= 0; r7–)
    • rpi2:
      • L1 d-cache: 0x7f, 0x7e, ……, 0까지 루프 수행(-1이면 루프2 탈출)
      • L2 d-cache: 0x3ff, 0x3fe, ……, 0까지 루프 수행(-1이면 루프2 탈출)
loop2:
                mov     r9, r4                  @ create working copy of max way size 
loop3:
 ARM(           orr     r11, r10, r9, lsl r5    ) @ factor way and cache number into r11
 ARM(           orr     r11, r11, r7, lsl r2    ) @ factor index number into r11
 THUMB(         lsl     r6, r9, r5              )
 THUMB(         orr     r11, r10, r6            ) @ factor way and cache number into r11
 THUMB(         lsl     r6, r7, r2              )
 THUMB(         orr     r11, r11, r6            ) @ factor index number into r11
                mcr     p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
                subs    r9, r9, #1              @ decrement the way
                bge     loop3
                subs    r7, r7, #1              @ decrement the index
                bge     loop2
  • mov r9, r4
    • r9(loop3용 way 카운터): r4(현재 캐시 레벨의 way 값-1)를 사용하여 0까지 루프를 돈다.
      • for (r9 = r4; r9 >= 0; r9–)
    • rpi2:
      • L1 d-cache: 3, 2, 1, 0까지 루프 수행(-1이면 루프3 탈출)
      • L2 d-cache: 7, 6, …, 0까지 루프 수행(-1이면 루프3 탈출)
  • orr r11, r10, r9, lsl r5
    • r11: DCCISW 캐시 삭제 명령을 수행하기 위해 필요한 r9(Way) 및 r10(캐시 레벨값)을 저장.
  • orr r11, r11, r7, lsl r2
    • r11: DCCISW 캐시 삭제 명령을 수행하기 위해 필요한 r7(Set)값을 저장.
  • mcr p15, 0, r11, c7, c14, 2
    • DCCISW(Data Cache Clean & Invalidate by Set/Way)를 사용하여 캐시 한 라인을 삭제
  • subs r9, r9, #1
    • way 카운터를 1 감소
  • bge loop3
    • r9 값이 0보다 같거나 크면 loop3로 다시 반복
  • subs r7, r7, #1
    • NumSets 카운터를 1 감수
  • bge loop2
    • r7 값이 0보다 같거나 크면 loop2로 다시 반복
skip:
                add     r10, r10, #2            @ increment cache number
                cmp     r3, r10
                bgt     loop1
finished:
                ldmfd   sp!, {r0-r7, r9-r11}
                mov     r10, #0                 @ swith back to cache level 0
                mcr     p15, 2, r10, c0, c0, 0  @ select current cache level in cssr
  • add r10, r10, #2
    • r10(캐시 레벨 카운터)을 2 증가 시킨다.
  • cmp r3, r10
    • r3(Loc x 2)와 r10(캐시 레벨 카운터)를 비교
  • bgt loop1
    • r3(Loc x 2)가 r10(캐시레벨 카운터)보다 큰 경우 loop1으로 다시 반복
    • rpi2: LoC=L2 이므로 L1에서 Loc(L2) 캐시 레벨까지 수행한다. (두 번의 루프가 수행)
  • ldmfd sp!, {r0-r7, r9-r11}
    • 백업해두었던 레지스터들을 복원한다.
  • mov r10, #0
    • 동작할 캐시 레벨을 다시 L1(0) 처음으로 돌리려한다.
  • mcr p15, 2, r10, c0, c0, 0
    • CSSELR 레지스터로 동작할 캐시 레벨을 지정한다.

iflush:

i-cache는 한 번에 flush 한다.

iflush:
                mcr     p15, 0, r10, c7, c10, 4 @ DSB
                mcr     p15, 0, r10, c7, c5, 0  @ invalidate I+BTB
                mcr     p15, 0, r10, c7, c10, 4 @ DSB
                mcr     p15, 0, r10, c7, c5, 4  @ ISB
                mov     pc, lr
  • mcr p15, 0, r10, c7, c10, 4
    • DSB (Data Synchronization Barrier operation)를 사용하여 이미 동작중인 모든 캐시 조작 명령이 완료될 때 까지 기다린다.
  • mcr p15, 0, r10, c7, c5, 0
    • ICIALLU(Instruction Cache Invalidate ALL for LoU)를 사용하여 i-cache를 모두 비운다.
  • mcr p15, 0, r10, c7, c10, 4
    • DSB
  • mcr p15, 0, r10, c7, c5, 4
    • ISB(Instruction Synchronization Barrier operation)를 사용하여 명령 파이프 라인을 비운다.
  • mov pc, lr
    • 호출한 곳으로 리턴한다.

 

참고

댓글 남기기