kernel/head.S – __fixup_smp:

SMP(Multi core) 운용을 위해 빌드한 커널이 실제 사용 시 UP(Uni core)에서 동작을 시키는 경우 SMP 전용 명령들을 모두 UP 명령으로 치환(fixup)해주는 루틴

  • 2010년 9월 kernel v2.6.37-rc1에 처음 추가되었고 2011년 1월 v2.6.38-rc3에서 추가 보강되었다.

 

전체 순서도

kernel_head.s분석5

 

UP 시스템 체크

__fixup_smp:

CPU id를 분석하여 4가지 조건 case에서 매치되는 경우 UP 시스템으로 인식하여 __fixup_smp_on_up: 루틴으로 이동하여 처리하고 SMP인 경우 루틴을 빠져나간다.

1) UP case A: ARMv7이 아니면 UP

MIDR을 분석하여 ARMv7이 아니면 UP로 판단한다.

#ifdef CONFIG_SMP_ON_UP
        __HEAD
__fixup_smp:
        and     r3, r9, #0x000f0000     @ architecture version
        teq     r3, #0x000f0000         @ CPU ID supported?
        bne     __fixup_smp_on_up       @ no, assume UP

MIDR을 읽어서 MIDR.architecture가 f가 아니면(armv7이 아니면) UP에서
* 동작한 것이라고 판단한다.

  • and r3, r9, #0x000f0000
    • r9(MIDR, cpu id)에 비트마스크 0x000f_0000(MIDR.architecture)를 한다.
    • MIDR
      • Implementer[31..24]: 0x41 =ARM
      • Variant[23..20]: 0x0 =Major revision number
      • Architecture[19..16]: 0xf =ARMv7
      • Primary part number[15..4]: 0xc07 =Cortex-A7 MPCore part number
      • Revision[3..0]: 0x3 =Minor revision number
  • teq r3, #0x000f0000
    • MIDR.architecture와 ARMv7(0x000f_0000)을 비교한다.
    • tst는 and 연산 후 상태 반영, teq는 xor 연산 후 상태 반영
  • MIDR.architecture가 ARMv7이 아니면 __fixup_smp_on_up으로 이동

 

2) ARM11MPCore인 경우 SMP

ARMv7인 경우 이 루틴에 도착하는데 cpu id를 분석하여 ARM11MPCore 인지 확인되면 SMP라 판단되어 루틴을 종료한다.

        bic     r3, r9, #0x00ff0000
        bic     r3, r3, #0x0000000f     @ mask 0xff00fff0
        mov     r4, #0x41000000
        orr     r4, r4, #0x0000b000
        orr     r4, r4, #0x00000020     @ val 0x4100b020
        teq     r3, r4                  @ ARM 11MPCore?
        reteq   lr                      @ yes, assume SMP
  • bic r3, r9, #0x00ff0000
    • r3 = MIDR.Variant 및 MIDR.Architecture 비트 clear
    • r3 = MIDR.Revision 비트 clear
  • r4 = #0x4100_b020
  • teq r3, r4
    • MIDR.Implementer가 0x41이고 MIDR.Primary part number가 0xb02이면 ARM11MPCore로 판단한다.
  • reteq lr
    • 이 프로세서는 MP 프로세서 이므로 루틴을 더 이상 실행하지 않고 종료.

 

3) UP case B: Multiprocessing Extension 포함되었지만 UP로 구성된 시스템은 UP

ARM11MPCore가 아닌 경우 이 루틴으로 오게되는데 아마 Cortex 시리즈들일 것이라 판단된다. MPIDR을 읽어 Multiprocess Extensions이 설정되어 있지만 UP로 구성되어 동작하게 되어 있는 시스템은 여기서 UP로 판단한다.

        mrc     p15, 0, r0, c0, c0, 5   @ read MPIDR
        and     r0, r0, #0xc0000000     @ multiprocessing extensions and
        teq     r0, #0x80000000         @ not part of a uniprocessor system?
        bne    __fixup_smp_on_up        @ no, assume UP
  • mrc p15, 0, r0, c0, c0, 5
    • MPIDR(Multi Processor Affinity Register)를 읽는다.
  • and r0, r0, #0xc0000000
    • MPIDR.Bits[31]: Multiprocessing Extensions 구현 포함 여부(0=미포함, 1=포함)
    • MPIDR.Bits[30]: 0=MP, 1=UP
  • teq r0, #0x80000000
    • Multiprocessing Extensions가 구현되었지만 UP인 경우 __fixup_smp_on_up 루틴으로 이동

 

4) Cortex-A9 MPCore가 아니면 SMP

Cortex-A9 아키텍처는 MP용도로 설계되었다. 그러나 실제 SoC 제작 시 1 코어 용으로 설계된 사례가 있다. 그래서 이경우만 아니면 SMP로 판단한다. (Aegis 플랫폼에 사용된 SoC 중 하나가 ARM Cortex-A9 with single core)

  •  여기서 부터 추가된 조건들은 2013년 9월 kernel v.3.12-rc6에 추가된 항목이다.
        @ Core indicates it is SMP. Check for Aegis SOC where a single
        @ Cortex-A9 CPU is present but SMP operations fault.
        mov     r4, #0x41000000
        orr     r4, r4, #0x0000c000
        orr     r4, r4, #0x00000090
        teq     r3, r4                  @ Check for ARM Cortex-A9
        retne   lr                      @ Not ARM Cortex-A9,
  • r4 = 0x4100_c090
  • teq r3, r4
    • ARM Cortex-A9가 아닌 경우 루틴을 빠져나간다.
5) UP case C – Cortex-A9 이면서 IO base 주소가 0이면 UP
        @ If a future SoC *does* use 0x0 as the PERIPH_BASE, then the
        @ below address check will need to be #ifdef'd or equivalent
        @ for the Aegis platform.
        mrc     p15, 4, r0, c15, c0     @ get SCU base address
        teq     r0, #0x0                @ '0' on actual UP A9 hardware
        beq     __fixup_smp_on_up       @ So its an A9 UP
  • mrc p15, 4, r0, c15, c0
    • Cortex A9이 UP로 설계되어 있는지 확인하기 위해 CBAR에서 IO 장치의 base 주소를 가져온다.
    • IO base 주소가 0이면 UP 이므로 __fixup_smp_on_up 루틴으로 이동한다.
6) UP case D – Cortex-A9 이면서 CPU 수가 1개이면 UP
        ldr     r0, [r0, #4]            @ read SCU Config
ARM_BE8(rev     r0, r0)                 @ byteswap if big endian
        and     r0, r0, #0x3            @ number of CPUs
        teq     r0, #0x0                @ is 1?
        retne   lr
  • ldr r0, [r0, #4]
    • IO base 주소 + 4의 위치에서 SCU 설정값을 읽어온다.
  • and r0, r0, #0x3
    • 이 값의 하위 2비트로 CPU 수를 표현한다.
  • teq r0, #0x0
    • 이 값이 0이 아니면 SMP로 판단하여 루틴을 빠져나간다.
      • 0=CPU 1개, 1=CPU 2개, 2=CPU 3개, 3=CPU 4개
    • 이 값이 0이면 UP로 인식하여 아래 루틴(__fixup_smp_on_up:)으로 계속 진행한다.

 

ALT_SMP() & ALT_UP() 매크로

ALT_SMP() 매크로는 SMP에서만 동작하는 명령이 들어가고 ALT_UP() 매크로는 UP 시스템에서만 동작하는 명령이 들어간다.

  • SMP 시스템에서는 smp_func() 함수를 수행하는데 UP 시스템에서는 아무것도 하지 않을 때의 예)
    • ALT_SMP(bl smp_func);
    • ALT_UP(nop);

kernel_head.s분석4

두 매크로의 규칙은 다음과 같다.

  • ALT_SMP()와 ALT_UP()는 항상 짝을 이루어 붙어 다닌다.
  • ALT_SMP()와 ALT_UP()에 들어가는 명령(instruction)은 항상 4바이트 이다.
  • ALT_SMP()는 실제 코드섹션 영역에 그대로 저장된다.
  • ALT_UP()는 .alt.smp.init 섹션에 4바이트씩 두 번 push되는데 처음 word는 ALT_SMP()의 명령주소를 가리키고 있고(나중에 치환할 주소를 알아내기 위함), 두 번째 word는 동작할 UP 코드(명령)이다.

SMP 코드를 UP 코드로 fixup

__fixup_smp_on_up:

__fixup_smp_on_up:
        adr     r0, 1f
        ldmia   r0, {r3 - r5}
        sub     r3, r0, r3
        add     r4, r4, r3
        add     r5, r5, r3
        b       __do_fixup_smp_on_up
ENDPROC(__fixup_smp)

        .align
1:      .word   .
        .word   __smpalt_begin
        .word   __smpalt_end
  • r3: offset
  • r4: __smpalt_begin
  • r5: __smpalt_end
  • 위의 레지스터를 준비한 후 __do_fixup_smp_on_up: 루틴으로 이동

 

__do_fixup_smp_on_up:

이 루틴에서 루프를 돌며 SMP 코드를 UP 코드로 치환한다.

        .text
__do_fixup_smp_on_up:
        cmp     r4, r5
        reths   lr
        ldmia   r4!, {r0, r6}
 ARM(   str     r6, [r0, r3]    )
 THUMB( add     r0, r0, r3      )
#ifdef __ARMEB__
 THUMB( mov     r6, r6, ror #16 )       @ Convert word order for big-endian.
#endif
 THUMB( strh    r6, [r0], #2    )       @ For Thumb-2, store as two halfwords
 THUMB( mov     r6, r6, lsr #16 )       @ to be robust against misaligned r3.
 THUMB( strh    r6, [r0]        )
        b       __do_fixup_smp_on_up
ENDPROC(__do_fixup_smp_on_up)
  • cmp r4, r5
    • r4(__smpalt_begin으로 시작되는 카운터)와 r5(__smpalt_end)와 비교
  • reths lr
    • 끝까지 간 경우 종료(r4 >= r5)
  • ldmia r4!, {r0, r6}
    • r4 카운터 주소가 가리키는 곳에서 2 워드를 r0와 r6레지스터로 읽어오고 r4 카운터 주소를 1 word 감소시킨다.
    • r0: ALT_SMP에 있는 instr 주소
    • r6: ALT_UP에 있는 instr 값
  • str r6, [r0, r3]
    • r6(ALT_UP에 있는 instr 값)을 r0(ALT_SMP를 가리키는 instr 주소) + r3(offset)  주소에 저장한다.
  • b __do_fixup_smp_on_up
    • 루프를 돌기 위해 처음으로 이동한다.

 

댓글 남기기