009주차 (2013-06-22) Arm System Developer's Guide (Ch14~끝)


#1

a10c (2013-06-22) 009주차

  • 참석자: 25명
  • 장소지원: NHN D2 개발자 커뮤니티 지원
  • 장소: 토즈강남
  • 장소예약: 김성학님
  • 소스 드라이빙: 박영기님
  • 식사: 신의주찹쌀순대 (순대국밥: 6,000원, 돈까스: 6,000원)

진도

  • “Arm System Developer’s Guide” / (~Ch.14.7, 캐시와 쓰기 버퍼 진행 중)

내용

  • 기 선정된 Chapter 위주로 진행
  • git에 대한 간략한 설명 및 토론진행 (박영기님)
  • 대다수 인원이 노트북을 지참하여, git 환경설정 시도
  • Kernel 코드는 3.9.6으로 fix

Q&A

  • FCSE에서 High vector를 쓰는 이유는? Low vector를 쓰면 안되는 것인가?

MVA = VA | (PID « 25) ········ (1) (1)의 공식에서 PID의 값을 바탕으로 연산이 되기 때문에 low vector는 성립이 불가능

  • CP15라는 말이 자주 나오는데, 이것이 대체 무엇이고, 어디에 위치해 있는가?

물리적으로는 core안에 존재하며, CP는 co-processor의 의미이다. ARM과 CP간에는 어셈블리 명령에 의해서 서로를 동작시킬 수가 있으며, 주로 CP 명령어로 MMU나 cache를 동작시킨다. CP0~15까지 CP를 부착할 수 있으며, CP15의 명령어는 TRM문서를 확인하면 동작을 자세히 볼 수 있다.

  • ctags나 cscope 설정 시, 만약 파일이 바뀌면, tags나 cscope를 다시 해줘야 하는가?

그렇다. 그래서 보통 kernel은 그대로 두고, 다른 코드는 만들때 붙여서 사용하면 편하다.

  • 진행하다가 소스코드가 바뀐부분만 어떻게 받는가?

git log 를 써서 바뀐 부분을 체크하고, git pull 명령을 사용해서 받아온다.

  • Windows git을 써도 되나?

사용은 할 수 있지만, 대소문자가 다른 name이 있어서 linux 환경에서 사용하는 것이 좋을 것 같다.

  • 왜 head.S가 처음 시작되나?

링커스크립트 안에 해당 내용이 언급된다. arch/arm/boot/compressed/vmlinux.lds 파일의 ENTRY를 보면 start가 존재한다.

  • 그래도 잘 이해가 안간다. 커널의 진정한 시작이 head.S가 정말 맞는 것인가?

정말 이해하려면 U-boot의 링커부분을 확인하면 된다. 일단은 여기가 처음이라고 가정하고 넘어가자.

  • 그렇다면, arm/kernel/head.S 파일과는 또 뭐가 다른것이냐??

arm/kernel/head.S는 linux 커널(vmlinux, Image)의 entry (_start) arm/boot/compressed/head.S는 압축한 커널 binary와 decompressing module을 포함한 이미지(vmlinux, zImage)의 entry (stext) bootloader가 jump하는 곳은 arm/boot/compressed/head.S 참조: 커널분석 시작점

  • assembly에서 .의 의미가 무엇인가?? .은 전부 섹션이라고 볼 수 있는 것인가?

지시어의 의미로, GCC compiler 메뉴얼에 나와있다. 정확하게 GNU as 매뉴얼을 보시면 됩니다.

  • 어셈은 아키텍쳐마다 다른것으로 알고 있는데, GNU는 공통인가?

여기서 어셈이 명령어를 말하는 것이라면, ARM 매뉴얼에 있는 명령어는 그대로 사용할 수 있습니다. 컴파일러 지시어(디렉티브)는 컴파일러마다 다르고, 또 GCC는 다양한 타겟을 지원해서 같은 디렉티브라도 타겟에 따라 그 문법이 달라지는 것이 있습니다.

  • .macro가 대체 뭐냐?

.macro는 C에서 define 개념이라고 보면 된다. .macro ~ [매크로내용] ~ .endm 을 통해 macro을 지정한다.

29    .macro  writeb, ch, rb
30    mcr p14, 0, \ch, c0, c5, 0
31    .endm

를 살펴보면, writeb라는 명령을 실행하면, “mcr p14, 0, \ch, c0, c5, 0”가 실행된다.

  • 이제 좀 알것 같다. 그럼 \의 의미는 무엇이냐?

위의 코드를 예로 들면, ch 인자값을 사용하겠다는 의미이다. macro 선언시 사용한 매개변수 이름을 정의 내에서 사용할 경우 매개변수 이름 앞에 적용 참조: gas directive 정리 pdf 중 “\arg”

  • 우린 여지껏 CP15만 얘기했는데, CP14는 또 뭐냐? 어디서 나온 것이냐?

CP14는 Debug관련 명령어를 처리하는 co-processor이다. ※ CP14의 명령어 및 동작내용은 wiki에서 다룰 내용은 아니고, 주석에 언급하였기 때문에, 주석을 참고하시기 바랍니다. 10차 ARM kernel C팀 소스 저장소/head.S

  • 초반에 8번 반복을 시행하는데, 왜 하는 것이냐?

아마 Pipeline flush를 위해서 하지 않을까 싶다. 조사가 필요하다.

125         .rept    7
126         mov  r0, r0
127         .endr
128    ARM(    mov r0, r0 )

질문> mov r0,r0 mov r0, r0 8번 관련 코드변경이력…

Author: Russell King - ARM Linux Date: 2006-08-02 23:10 +900 To: liang ming-chuan CC: linux-arm-kernel Subject: Re: the purpose of repeat mov r0, r0 On Wed, Aug 02, 2006 at 09:48:35PM +0800, liang ming-chuan wrote:

hi, what’s the purpose of reapeat mov r0, r0 eight times in arch/arm/boot/compressed/head.S?

Some boot loaders used to call the kernel at 0xNNNN8020 not 0xNNNN8000. Then, a set of magic numbers were introduced at 0xNNNN8024…30, which makes removing these 8 instructions problematical.

옛날옛날에 어떤 거지같은 부트로더가 있었는데 그놈은 이미지 올려놓은 램주소에서 8 Word(32byte = 0x20)만큼의 하위 주소를 call해 버렸었고 올려놓은 주소에서 offset(0x24~0x30)사이에는 magic number를 찾았다는군요… 부글부글… 그래서 그런 거지같은 부트로더들 맞춰줄려고 head.s에 의미없는 instruction(mov r0, r0)를 8번(8 Word)해서 소비해주시고, 뜬금없고 의미없는 8번의 삽질후 .word 0x016f2818 @ Magic numbers to help the loader .word start @ absolute load/run zImage address .word _edata @ zImage end address 매직넘버와 address를 offset(0x24~0x30 = 12Byte = 3Word)에 집어넣는 구조가 되어버렸다는 것같군요. 부글부글…

  • 위의 내용은 이해가 되었다. 하지만 ARM모드인 경우엔 0x20 확실하게 건너뛰고 branch가 시작되는데, Thumb2 모드인 경우엔 ARM(…) 코드들은 없어지니까 0x1C 주소부터 adr … 명령이 자리잡을 것 같은데 이유가 궁금하다.

head.o를 ARM모드와 Thumb-2모드로 컴파일 해 보았어요. 가장 큰 차이점은 nop이 ARM모드에서는 8번, Thumb-2모드에서는 7번 반복되었다는 것 정도?

그리고 Thumb-2 모드:

$ arm-linux-gnueabihf-objdump -d arch/arm/boot/compressed/head.o
00000000 <start>:
   0:	e1a00000 nop	 ; (mov r0, r0)
   4:	e1a00000 nop	 ; (mov r0, r0)
   8:	e1a00000 nop	 ; (mov r0, r0)
   c:	e1a00000 nop	 ; (mov r0, r0)
  10:	e1a00000 nop	 ; (mov r0, r0)
  14:	e1a00000 nop	 ; (mov r0, r0)
  18:	e1a00000 nop	 ; (mov r0, r0)
  1c:	e28fc00d add	ip, pc, #13
  20:	e12fff1c bx	ip
  24:	016f2818 .word	0x016f2818

thumb-2로 컴파일 했을때 0x20 주소에서 시작하는 것이 이상해보입니다. ip(=r12)에 어떤값이 있을지…

현재 thumb2 모드의 이상한 코드는 예전의 그 부트로더와는 상관없는, 또다른 부트로더를 대응하기 위한 workaround라고 합니다. http://goo.gl/SwF4y 따라서 thumb2 모드에서는 0x20번지로 점프를 하는 시나리오는 존재하지 않는다고 보면 될 것 같습니다.

  • 이제 clear하다. 근데 컴파일러가 알아서 mov r0 r0를 nop로 바꾼걸로 해석하면 되는 것인가?

armv5 까지는 nop 명령어가 없었습니다. 그런데 armv7으로 오면서 생겼어요. (armv6에서 생겼는지도 모르겠어요) (ARMv6K, ARMv6T2) 그런데 프로그래머가 소스에 nop 명령을 쓰면 컴파일러가 알아서 mov r0, r0 로 바꾸어서 컴파일 해줍니다. 이런 명령어를 아마 pseudo instruction이라고 부를겁니다.

—————————————————————- H.3 Pre-UAL pseudo-instruction NOP

In pre-UAL assembler, NOP is a pseudo-instruction, equivalent to: • MOV R0, R0 in the ARM instruction set • MOV R8, R8 in the Thumb instruction set.

Assembling the NOP mnemonic as UAL will not change the functionality of the assembled software, but will change: • the instruction encoding selected • the architecture variants on which the resulting binary will execute successfully, because the NOP instruction was introduced in ARMv6K and ARMv6T2.

To avoid these changes, replace NOP in the assembler source code with the appropriate one of MOV R0, R0 and MOV R8, R8, before assembling as UAL. —————————————————————-

용어정의

What is the difference between an instruction and a pseudo-instruction?

Answer: A pseudo-instruction doesn’t actually exist in the instruction set of a processor. A pseudo-instruction will »>be a convenient single name for one or more actual instructions.

A common example is the unconditional jump instruction.

Normally the syntax for this instruction would be: jmp address

…but the assembler might actually translate that into: cmp t0 r0 r0 jmp t0 address

Which is basically checking to see if the zero register is equal to the zero register, and if so jump. Since »>this will always be true, it will always jump.

Unified Assembler Language (UAL) is a common syntax for ARM and Thumb instructions. It supersedes earlier versions of both the ARM and Thumb assembler languages. Code written using UAL can be assembled for ARM or Thumb for any ARM processor. The assembler faults the use »>of unavailable instructions. RealView® Compilation Tools (RVCT) v2.1 and earlier can only assemble the pre-UAL syntax. Later versions of »>RVCT and ARM Compiler toolchain can assemble code written in pre-UAL and UAL syntax. By default, the assembler expects source code to be written in UAL. The assembler accepts UAL syntax if any »>of the directives CODE32, ARM, THUMB, orTHUMBX is used or if you assemble with any of the –32, –arm, –»>thumb, or –thumbx command line options. The assembler also accepts source code written in pre-UAL ARM »>assembly language when you assemble with CODE32 or ARM. The assembler accepts source code written in pre-UAL Thumb assembly language when you assemble using the →»-16 command line option, or the CODE16 directive in the source code. Note The pre-UAL Thumb assembly language does not support Thumb-2 instructions.

e1a00000 nop; (mov r0, r0) 에서 opcode는 실제로 e1a00000 는 mov r0, r0의 코드입니다. 실제로 명령어는 mov r0, r0이지만 관례대로 nop로 디스어셈블 한것 처럼 보입니다.

            .arch   armv5
start:
            nop
            mov     r0, r0
            b       .

위 코드를 컴파일 하고 디스어셈블 해서보면

00000000 <start>:
    0:   e1a00000        nop                     ; (mov r0, r0)
    4:   e1a00000        nop                     ; (mov r0, r0)
    8:   eafffffe        b       8 <start+0x8>

이런 코드가 나옵니다. .arch armv7-a 로 바꿔서 컴파일 하면,

00000000 <start>:
    0:   e320f000        nop     {0}
    4:   e1a00000        nop                     ; (mov r0, r0)
    8:   eafffffe        b       8 <start+0x8>

이런 코드가 나옵니다.

  • 1f로 branch한다는 것은 또 무슨 말이냐?

1로 점프한다는 뜻이고, f는 forward, b가 붙으면 backward의 의미이다.

  • 이미 시작할때 ARM모드로 동작하는것 아니었나? THUMB모드 명령은 왜 있는것인가?
128      ARM(    mov r0, r0     )
129      ARM(    b      1f          )
130 THUMB(    adr   r12, BYSM(1f)    )
132 THUMB(    bx    r12       )

Thumb2의 경우 ARM에서 동작되니까, 아마 커널 컴파일에서 옵션으로 줄 수 있는것이 아니겠느냐? ⇒CONFIG_THUMB2_KERNEL 선언을 통해 커널을 Thumb2 모드에서 동작하도록 컴파일할수 있는 것으로 보입니다. 커널이 Thumb2모드에서 동작을 해도, ARM 모드에서 시작을 했기 때문에 스위칭을 하기 위해 위의 THUMB() 두 명령어는 실제로 ARM 모드으로 컴파일되는 코드 입니다. (.arm 이후에 있는 코드이기 때문에.) CONFIG_THUMB2_KERNEL 선언이 되었을 때, Thumb2 모드로 전환하기 위해 삽입되는 ARM코드입니다. CONFIG_THUMB2_KERNEL 선언이 되었다면, 1f라 지정된 곳이 thumb 용 코드의 시작입니다. THUMB(…)는 thumb용 코드가 아니라 CONFIG_THUMB2_KERNEL 선언이 되었을 때 삽입되는 코드입니다.

  • 좋다 그렇다고 치자. 그럼 ARM명령어를 실행하고 바로 THUMB명령이 존재하는데, 이건 어떻게 실행하느냐?

arch/arm/include/asm/unified.h에 보면 macro가 지정되어 있는데, ARM이든 Thumb이든 하나만 실행하게 되어있다.

  • 그렇다면, 다음 코드부터는 ARM과 Thumb모드가 공통으로 실행되는데, 가능한 것인가?

Thumb2는 둘다 사용 가능 하기 때문에 상관 없다. 토론을 통해 어떻게 가능한 것인지 짚고 넘어가야 합니다. 아래 링크를 보면 Thumb instruction set에서 32-bit ARM instructions subset을 지원한다고 하네요. 16비트 명령어지만 로스없이 32비트로 바꿔서 동작합니다. ARM Processor Architecture

그리고… UAL 이므로 arm/thumb 둘다 적용 가능해 보입니다.

0:	e1a00000 nop	 ; (mov r0, r0)
   4:	e1a00000 nop	 ; (mov r0, r0)
   8:	e1a00000 nop	 ; (mov r0, r0)
   c:	e1a00000 nop	 ; (mov r0, r0)
  10:	e1a00000 nop	 ; (mov r0, r0)
  14:	e1a00000 nop	 ; (mov r0, r0)
  18:	e1a00000 nop	 ; (mov r0, r0)
  1c:	e28fc00d add	ip, pc, #13
  20:	e12fff1c bx	ip
  24:	016f2818 .word	0x016f2818
...
  30:	f3ef 8900 mrs	r9, CPSR
  34:	f7ff fffe bl	0 <__hyp_stub_install>
  38:	460f      	mov	r7, r1
  3a:	4690      	mov	r8, r2
  3c:	f3ef 8200 mrs	r2, CPSR
  40:	f012 0f03 tst.w	r2, #3
  44:	f040 8003 bne.w	4e <not_angel>
  48:	f04f 0017 mov.w	r0, #23
  4c:	dfab      	svc	171	; 0xab

위와같이 thumb2로 disassemble한 code를 보면 32bit명령, 16bit명령 섞여있다. 그리고 처음엔 .arm으로 시작하고 나중에 THUMB(.thumb) 으로 전환합니다.

thumb2는 thumb 시절의 제약이 없어졌고, code size는 16bit, 32bit 가변적이다.

  • BSYM으로 mode change가 가능하지 않은가?

이때 질문 내용 및 답변 내용을 잘 이해하지 못하였습니다. 수정 부탁 드립니다. ⇒ BSYM은 파라메터(주소)에 1을 더해서 bx명령와 조합되므로 ARM→Thumb 또는 Thumb→ARM 모드 변경이 일어납니다. armv5 같은 아키텍쳐에서는 thumb용 어셈블리 코드와 arm 어셈블리 코드는 다르게 작성이 되어야 했습니다. 그런데 /arch/arm/boot/compressed/head.S 내용 중 ” THUMB( .thumb ) ” 코드는 이 이후의 코드는 ARM 코드나 Thumb코드로 컴파일 될 수 있음을 말합니다. 즉 한개의 어셈블리 소스로 ARM 코드, Thumb 코드 모두 컴파일 될 수 있다는 것을 알았습니다. (정확하게는 Thumb2)

  • Magic Number는 또 무엇인가?

Loader가 판단 하고, 엉뚱한 이미지가 올수도 있기 때문에 일정한 넘버를 체크하여 일치하는 이미지를 판단 가능함. ELF의 Header에서 Magic Number를 체크할 수 있다. head.S와 매직넘버

  • B명령과 BL명령어의 차이점은 무엇인가?

B는 단순한 branch이고, BL은 돌아올 주소 명령을 저장하고 branch하는 것이다.

  • arch/arm/boot/compressed/vmlinux.lds에 언급된 ENTRY(_start)를 통해 head.S의 start가 시작점 이라고 했는데, _start와 start는 다른 것 아닌가?

arch/arm/boot/compressed/vmlinux.lds

ENTRY(_start)
:
  .text : {
    _start = .; <== 이 것에 의해서 섹션 .start 의 젤 처음이 _start 심볼로 정의 된다고 보는 것이 맞는 것 같습니다.
    *(.start)
:

그리고 실제 코드에서 start 심볼이 섹션 .start의 젤 처음에 놓이게 되는 것이구요.

arch/arm/boot/compressed/head.S

        .section ".start", #alloc, #execinstr
        .align
        .arm                @ Always enter in ARM state
start:
        .type   start,#function
        .rept   7
        mov r0, r0
        .endr

#3

9주차 Study Note (2013/06/22)

Status

Notice

  • 기 선정된 Chapter 위주로 진행
  • git에 대한 간략한 설명 및 토론진행
  • 노트북을 지참하여 git 환경설정 시도
  • Kernel 코드는 3.9.6으로 fix

Issues (Clear)

  • [x] 1. 인터럽트에서 VIC 컨트롤러는 ARM에서 항상 쓰는가? [#53]

    설계하는 것은 vendor 마음이기 때문에, 비슷한 기능을 가진 컨트롤러를 쓰기도 한다.


  • [x] 2. 논리캐시 vs. 물리캐시 중 어떤캐시가 더 좋은것인가? [#54]

    논리캐시는 타이밍 관점에서 좋지만 aliasing 문제가 있기 때문에, 최근에는 물리캐시로 움직이고 있다.


  • [x] 3. Cache lock은 일반 program에서 가능한가? 그리고 실제로 사용되는 예제가 있을까? [#55]

    불가능하다. 보통 exception handler에서 사용한다. 보통 RTOS에서 사용하긴 하지만, Linux에서는 사용할 일이 없을까 생각한다.


  • [x] 4. Memory-mapped 방식이 이미 하드웨적으로 address가 셋팅된 것이 아닌가? 소프트웨어 적으로 변경이 가능한가? [#56]

    기본적으로 Physical Address는 고정되어 있다. 단 MMU를 통해 Virtual Address mapping을 변경할 수 있다. (예외: 일부 SoC의 경우 bridge단에서 register 설정으로 slave port별로 base address및 size를 변경하기도 한다.)


  • [x] 5. 메모리의 상태 표시 및 수정하기 위해 사용하는 Peek & Poke 방식이란? [#57]

Peek & Poke는 memory read/write에 대한 BASIC적인 표현입니다. IO mapped IO의 경우 IO가 따로 있어서 IO read/write와 구분하기 위한 표현입니다. 참고로 ARM은 memory mapped IO이므로 별도의 IO read/write 명령어가 필요없습니다.

integer_variable = PEEK(address)

POKE address, value

(Ref.: [PEEK and POKE] (https://en.wikipedia.org/wiki/PEEK_and_POKE))